Tuesday, December 9, 2014

After Years of C++ and Java, I Chose Python

After Years of C++ and Java, I Chose Python

Introduction

 I have been in the software business for quite a long time. When I started high-level languages (HLLs) were fairly new.  I spent a few years at Bell Labs, where C and Unix were invented, and then spent many of the following years at IBM, with a few other stops along the way.  Although in college, we were taught some HLLs, initially my jobs involved specialized assembly languages.  After quite a few years, I was able to start using HLLs in my work.  In the late 1980s and early 1990s, I was hearing a lot of buzz about object-oriented (OO) programming.  I took a graduate level course in object-oriented design and programming and I was hooked.  In this course I was introduced to Smalltalk and saw a demonstration of how productive one could be in that environment.

I believed object-oriented analysis, design, and implementation was the way to do software. Although it perhaps may not have lived up to all its hype, I still believe it is generally the best way to design and implement most systems.  I was pretty much consumed with learning everything I could about OO and becoming as much of an expert as possible.  I have over 20 books dealing with OO, including many about C++ and quite a few dealing with Java.  I went to OOPSLA and saw presentations by Grady Booch, Peter Coad, and others.  I studied various techniques for the design of OO software, especially techniques based on entity-relationship (ER) models.  I started using C++ when only preprocessors (like Cfront) were available, and then moved onto using real C++ compilers.  In the mid to late 1990s IBM made a big move into Java, and so did I. 

I have never lost my love of programming. I turned down a chance to go into management, and even when I had significant project leadership roles, I made sure I was still had a major part in writing code. A while back I retired early, but recently decided to go back into the workforce.  I wanted to find a job that combined my interest in Linux and programming.  Based on my recent (15+ years) of Java experience (especially J2EE), that would be the area that would have made the most sense for me.  Or, even though my experience was not as recent, C/C++ would be another area that would make sense since I had a lot of experience with this as well.  However, despite the fact that I had used Python for less time and mostly as a scripting language (especially using jython with WebSphere), I decided to consider that as well. I had recently started writing more scripts in Python instead of bash on my Linux systems. (I made the move to Linux about 15 years ago).

In preparing for potential jobs, including interviews and proficiency tests, I re-read various texts on Java/J2EE, C++, and Python.  Some of my Python texts were out-of-date (10 years old), so I got newer ones.  As I went through this  refresher stage, I was able to compare the languages.  Although I started out thinking I would most like to program using C++ again, after a while I came to a different conclusion: I really wanted a job where I used Python.  In this post, I will describe some of the reasons why, as well as mention some of my doubts and concerns.

Personal Perspective and Static Typing

In comparing C++, Java, and Python one should consider his or her own perspective when making comparisons.  If I have listened to a piece of music many times, and then I hear it played differently (perhaps a different tempo), it can sound, well, wrong.  It doesn't mean there is actually something wrong, but because I am used to it one way and I experience it done a different way, it doesn't seem right.  As I mentioned earlier, I have spent a lot of time in the past studying OO techniques and languages.  I have spent a lot of time learning and using C++ and Java, and there are a lot of similarities in how they do things.  In assessing Python, I had to guard against presuming that something done differently was wrong.

Another thing I had to keep in mind, was how the approach to Computer Science and programming have changed over the years.  When I studied Computer Science there was a lot of research around writing provably correct programs.  One famous topic was the famous letter by Edsger Dykstra titled Go To Considered Harmful (see this Wikipedia discussion).  We were taught that a function or any program block should have one entry and one exit point. Thus, the idea of breaking out of a loop or returning from the middle of a block was a sign of poor programming practice.  Proving even simple programs was a painstaking and tedious process, without having to deal with multiple exit points.  For many years, I religiously made sure to use a result variable rather than return early and I would structure my loops so I never had to use break.  There was even an initiative in the mid-1980s at IBM to start using program proof techniques on IBM software. This initiative died very quickly.  Proving programs has been shown to be impractical, at least on a large scale in competitive environments.

Another aspect that was de rigeur, at least for serious languages, was static typing.  You had to declare all your variables ahead of time, including their type.  The compiler would check the type at compile time (not run-time) avoiding the possibility that at run-time the wrong type (primitive type, structure, etc.) could be passed into a function for example.  Clearly this would eliminate a whole class of programming errors.  Of course, I never heard much discussion of any downsides to this approach.

In C++, and much later in Java, came or generic type support.  That is you could define some type of class, like a linked list.   You define your linked list class in terms of a generic type T.  When you declare an actual instance of the class of you specify the actual real type that replaces the generic type T. So for example,  C++, your class declaration might start like this:

       
template <class T>
class LinkedList {
public:
   LinkedList();
   ~LinkedList();
   T remove();
   void add(const T&);
   ...
}
       
 

Using such code, it is possible to create instances of LinkedList of Widget class, etc. Of course, typical implementations might have a LinkedListItem template class as well. This template class (generic type) support guarantees that we can make sure at compile-time that my LinkedList that is supposed to contain Widget instances will in fact only contain Widget instances. For a long time, I thought this type of language support was critical and I was frustrated that it was not implemented earlier in Java. 

Against this backdrop, are various more dynamic languages, including scripting languages and, well, Python.  Many people call Python just a scripting language and many people use it solely for scripting.  In fact, I have and still use it for scripting. I now use it instead of bash for anything but the simplest scripts.  There is a lot of discussion about strongly typed versus weakly typed languages. Some say Python is weakly typed and others say it is actually strongly typed.  For example, Python does check types and run time so that, for example, you can not add a number to a string without explicitly forcing a conversion.  Other even more dynamic languages will do the conversion for you.  While this may mean that technically Python is strongly typed, I do know that Python is not statically typed. That is, any type checking is done at run-time, not compile-time.

Given my education, training, and experience (mostly in enterprise software), it is surprising to me that I would choose Python, especially after all these years. In this article I will compare Python with some other languages and describe what I found so compelling.

Object-Oriented Approach

Primitive Types and Objects


C++ and Java have both primitive types, and support for objects (via classes). The fact that C++ has primitive types line char, int, etc. is not at all surprising. After all C++ was an enhancement of C and one of its tenets was that it inter-operate with programs written in C.

In Python, everything is an object.   So,  when you do the following:
       
val = 2
       
 

what you actually assigning is an object of type int, not simply a value of 2 to the name val. While this may not be as efficient in space or overhead as assigning an integer value to a variable in C++ or Java, it is consistent. Unlike other languages, with Python you know you don't have keep in mind whether a given value is a primitive type versus some type of object. Note that functions too are objects.  For example:

       

def triple(arg):
    return arg * 3

t = triple
print t(2)
       
 

results in the value 6 being printed. This makes creating callback functions trivial. Note that this works for member functions in classes as well.  When you assign an instance's member function to a name and call it, it knows the instance object as well (via the im_self variable in Python 2, or __self__ in Python 3). So you simply invoke it as usual (without having to pass in the instance yourself).

Method Overloading

In C++ and Java, method overloading is supported. That is, you can have the same method (member function) name with different numbers and types of arguments.  In actuality, internally the names are not the same. The name is mangled to include the types of the arguments. But to the programmer, the method name can be the same as long as the number or type of arguments is different. In Python, there is not a compilation phase which understands the type of arguments. So basically, without using something like decorators (more on these later), method overloading is not supported. (Note that there is an overload package that does use decorators to provide overloading support.)

Although I am used to being able to overload methods and have found it useful, using different method names (perhaps using the same common name with different suffixes) instead is not really a big deal to me.  However, there is one exception to this and that is the copy constructor.  The copy constructor is used to create a new instance of an object from another instance of the same type.  For example in C++, for a Widget class, the copy constructor would take the form:

       
Widget::Widget( const Widget & other)
{
...
}

       
 

There is no direct support for this in Python. It should be noted that technically Python does not have constructors, but instead has the __init__ method. Though not exactly the same as a constructor, the __init__ method is very similar. Like constructors, it is called to initialize a new instance of a given class following the use of the new operator (__new__ class method in Python). Of course in Python there are numerous ways one could choose to achieve the same results as a copy constructor in other languages.  One could use the most generic signature of the __init__ method:

       
class Widget(object):
    def__init__(*args, **kwords):
        ...
       
 

Using this generic approach of treating the arguments as a list, one could then, with various checks of the number (and if necessary) the type of arguments, achieve same result as having various different constructors, including a copy constructor. Using this approach one has to rely on documentation to allow the class user to differentiate between the different permutations. Another approach I have seen described, and have experimented with myself, is the use of a class static method. This method, used to create an instance of the class from another instance, would first call the class __new__ method and then copy the instance data to the new instance from the one passed in from which to copy. While this technique works, it does require one to remember (when writing the method) to invoke the __new__ method of the class.

As an alternative I actually prefer to use a decorator for the __init__ function. This decorator is a wrapper function that wraps __init__. It checks the first argument passed in, if any, and if it is an instance of the class the __init__ method is a part of, it invokes a different method, named _initcopy. Otherwise, it invokes the regular __init__ method.  A sample of this is shown below:

       
from __future__ import print_function
from functools import wraps
import copy

def with_copy_init(func):
 @wraps(func)
 def pickmethod(*args, **kwargs):
  """Wrapper for selecting initializer method"""
  if len(args) > 1:
   # For copy initializer, args[0] should be self
   if isinstance(args[1], args[0].__class__):
    # Call copy initializer. Call will automatically put self
    # as first argument
    args[0]._initcopy(args[1])
   else:
    # Call regular __init__ method
    func(*args, **kwargs)
  else:
   # Call regular __init__ method
   func(*args, **kwargs)
 return pickmethod
 


class MyClass(object):
 @with_copy_init
 def __init__(self, arg1, arg2):
  """Initializer"""
  print("*** MyClass __init__ method called ***")
  self.arg1 = arg1
  self.arg2 = arg2

 def _initcopy(self, other):
  """Copy initializer"""
  print("*** MyClass _createcopy method called ***")
  self.__dict__ = copy.copy(other.__dict__)

if __name__ == "__main__":
 inst = MyClass(1, 2)
 print("arg1 is", inst.arg1, "arg2 is", inst.arg2)
 cpy = MyClass(inst)
 print("in cpy, arg1 is", cpy.arg1, "arg2 is", cpy.arg2)
 print("Name of __init__ function is", cpy.__init__.__name__) 
 print("Docstring of __init__ function is", cpy.__init__.__doc__)
       
 

and here is the output from running this code:

       
*** MyClass __init__ method called ***
arg1 is 1 arg2 is 2
*** MyClass _createcopy method called ***
in cpy, arg1 is 1 arg2 is 2
Name of __init__ function is __init__
Docstring of __init__ function is Initializer
 

Note that to create an instance of MyClass with an existing instance, the invocation looks the same as creating the original instance. The only difference is that instead of the 2 original arguments, the sole argument is an instance of MyClass. Another interesting aspect of Python is that instance variables are put in the __dict__ instance variable (a dictionary).  So to copy in all the instance attributes from the original instance to the new one, it is sufficient to make a copy of __dict__. There may be cases where you want to be more selective in what is copied, but in many cases this is sufficient. Those who have coded in C++ and or Java will remember having to edit the copy constructor any time the set of instance variables was modified. Remembering to do this would normally not be necessary in Python using this decorator.

There are a couple of other things to note about this example. First, the wrap decorator from functools was not absolutely necessary. However, one of the nice things it does for you is copy the __name__ and __doc__ attributes from the function being wrapped (__init__ in this case) to the wrapper function (pickmethod). That way, if we reference __init__.__doc__ or __init__.__name__, we get the values we put in __init__, not what was in the wrapper function. Although this may not seem significant, it can reduce confusion when debugging.  Secondly, the decorator is called with_copy_init because it is not defining the copy initializer, but it is responsible for calling the copy initializer.  Note also that it relies on the class having a method named _init_copy. One would use this decorator if and only if they needed a copy constructor (initializer) and added the _init_copy method for this purpose.

Duck Typing

Coming from my previous work with C++ and (especially) Java, you get used to how things have to do work. If you want to register some type of callback function with another object (for example, a listener) you must define an interface for this purpose. For example, in Java one might define:

       
public interface MessageListener
{
    public void processMessage(String message);
}
       

The class that wanted to listen to messages would then have to implement the MessageListener interface and the class responsible for registering listeners would have to have a method (perhaps called addMessageListener)  that would be passed an instance of MessageListener.  In Python you can certainly do this as well. Although Python does not have special syntax for interfaces, it does have classes and you could define a simple MessageListener class and use multiple inheritance (MI) to get something akin to "implements MessageListener".  So, like other object-oriented languages, you could insist that implementers wanting to listen to messages conform to a given inheritance approach. However, this is not a requirement in Python.  Instead the code that originally handles the message could check to see if a given object had a processMessage method and if so, could just call it, assuming the code already had knowledge of this object.  This is a simple example of duck typing in Python. The name stems from the expression "if it walks like a duck, and it talks like a duck, ...".  Again, Python supports both approaches, while in Java you must use interfaces (unless you want to use something like Java introspection which is a fair amount of work). One approach requires you implement a particular type of class (or interface). If you instead inherit from a different class (or implement a different interface) which also has a processMessage method, that will not compile.  With the duck typing approach the processMessage method would still be called. I guess the question becomes do you want to insist that anyone listening to these messages declare that I am an instance of MessageListener? In Python you have a choice.

Handling of Properties

In Java, it is a common practice to access properties (attributes) of a class or instance via methods.  That is, there are get methods to get the value and there are set methods to set a value. A major reason for using such methods, rather than accessing the property directly, is encapsulation.  It is common practice in Java and C++ to declare properties as protected or private and only allow public access via methods. Part of the reasoning is that the data representation of the class may change but if you are using methods to access the data these underlying changes will not impact you. But this is only true if the name and signature of the existing methods still make sense (and are correct) in light of the change in the data representation. Often the case is that the data and the related methods all have to change.  Don't get me wrong, I believe very strongly in encapsulation.  In even fairly simple frameworks, it is extremely beneficial to be able to use objects for what they were intended, without having to know the internal details.  But in many cases the addition of get and set methods for attributes is done more by rote than for any meaningful reason.  Note there are other reasons for having get and set methods (JavaBeans, persistent objects, etc.), but I am not talking about those reasons here.

In Python it is often the practice that properties are accessed by name (i.e. directly) without using methods. In fact, there are decorators (including @property) which make it easy to access properties as simple field names even when a method is used under the covers. There is a whole discussion of the topic of how Python handles this compared to other languages here. When I first started doing extensive Python programming my first inclination was to use get and set methods for everything, as I had done in Java.  Now I no longer do so.  I think about whether the data is a property that should be publicly accessed or should be hidden, and that helps drive my decision making.  I have noticed that being able to access the properties more directly, especially when they are lists or dictionaries, has led to clarity in my code. Over time I will be able to better assess whether there is less encapsulation in my classes leading to the exposure of information that should be hidden. Please note that Python has naming conventions to identify protected and private properties, although things are not enforced to the extent they are in other languages.

One other thing I find interesting in Python is that if you have separate properties with like characteristics, instead of having to use decorators like @property, you can use a special type of class: Descriptor.  Depending on the situation descriptors can be very beneficial and reduce redundant code required when using decorators.

Context Managers

One of the things I found especially useful in C++ was using class scoping to automatically set some state when entering a class scope and reverting the state upon exit of the scope. A common example of this is using such a class to open a file at the beginning of the scope and closing it at the end.   This works because the file is opened in the constructor and closed in the destructor, at the end of the scope.  In Java this really can't be done in the same way because Java does not have destructors and the finalize method is not guaranteed to be called at the end of the scope (note I have not tried the try-with-resources block in Java 1.7).  In Python we can use Context Managers to do this.  Here is an example which works with a file:

       
with open("/tmp/tmp.dat, "w") as tempfile:
    print("hello world", file=tempfile)
    ....
# At this point the file is closed
       
 

You can write your own class in Python to be used with the with statement. You simply need to set your desired stated in the __enter__ method and restore the state in the __exit__ method. The __exit__ method may be passed an exception that was raised within that context, which it can handle or pass on. The use of context managers is useful for encapsulating the saving and restoring of state so the programmer can concentrate on what happens within that context and not worry about how to set up and restore the state of the context. The use of context manager leads to a more declarative style of programming. I will discuss this more later in this article.

Other Aspects of Python

Lists, Sets, Tuples, and Dictionaries

Many programming languages have various types of collection objects, including lists, sets, tuples, and dictionaries.  In Python these collections are used heavily throughout the language. It is common to see lists, tuples, or dictionaries returned from functions. For instance, in order to return multiple values from a function in many languages you would define a class to contain the data. In Python, you can just return a tuple containing the values. For example, suppose you wanted to return two names. Your function could simply do the following:

       
return (name1, name2)
       
 

Then, assume the calling code assigned the result of the function to the name result. You could do the following:

       
name1, name2 = result
       
 

Or you could iterate over the tuple:
       
for name in result:
    print name
       
 

Note that a key difference between tuples and lists is that they are immutable. This means that, unlike lists, tuples can be used as dictionary keys.

There is notational support for lists in Python too. For example,

       
mylist = [1, "dog", "cat", 3]
       

and again, iterating over a list is clearer than many other languages:

       
for e in mylist:
    print e
       

And of course there is notational support for dictionaries as well:

       
color_map = {1: "blue", 2: "red", 3: "green"}
for num, color in color_map.items():
    print num, "=>", color
       

You will note that the notational support adds both clarity and simplicity. It is easier to understand the code as well as remember the syntax. Of course, some other languages like Perl have similar notational support for lists, dictionaries, etc., but not languages like Java and C++.  As I got used to (and appreciated) the notational support for lists, dictionaries, etc. I thought about these other languages without such support. After all, the compilers for C++ and Java could be enhanced to support such notations.  But, then consider the whole static typing issue. Consider for instance my list example earlier. I had a mixture of numbers of and strings in the list.  Although Java does have a base object, java.lang.Object, the compiler could not assume that the type of data the coder desired in the list would be this base class. Instead the coder would likely want a list restricted to containing some more specific type. This specification must of course be declared.  (Note also that we are leaving out the whole issue of including primitive (non object) types of data in a list in languages like Java and C++.)

Here we are seeing a fundamental philosophical difference between dynamic languages like Python and statically typed languages like Java and C++. In Python you could choose to have a collection containing only a particular type of  object, but the language does not provide explicit support for declaring this and does not enforce this. If you want to enforce this you provide the code to do this yourself. Of course, there are approaches using decorators for checking types (see for example this package), plus early discussions of possible type checking approaches by Guido van Rossum, the founder of Python, here and here. At the current time I am appreciating the less restrictive style afforded by Python with the option of providing type checking as desired. Only time will tell whether I will see significant downside to this more flexible approach.

Decorators

I have mentioned decorators previously.  They are useful for adding additional functionality to functions, such as adding debug output every time a function is called. As I thought about some of the uses, I realized that some of these things were accomplished with preprocessor macros in C/C++.  Of course it would be possible to actually use a preprocessor with Python or use a package providing Python macro support such as MacroPy. You can't do everything with decorators that you could do with macros, but you can do a lot. There are many interesting modules that use decorators.  One of things I like about the decorators is that the notation (starting with '@') can add clarity to the code. It is also interesting to note that you can perform most if not all of what you can do with a macro preprocessor in Python by taking advantage of the Python Abstract Syntax Tree (AST), which is a representation of the source of a Python program.  Python allows this tree to be examined and modified before compiling, essentially allowing the structure and source of the program to be modified.  An example of its use is the ability to include pyDatalog syntax within a Python program.

Declarative Style

To be honest, in the past, I thought people describing a programming style as declarative were often using the term to mean "self documenting" just as a reason to avoid much commenting in their code. There are some languages that are declarative, such as Prolog (for now we'll just keep green versus red cuts out of the discussion--see the Bratko book). But I don't find languages like Java and C++ to be very declarative. At least C++, unlike Java, supports operator overloading which can make things a little more declarative.  Python supports operator overloading as well, but its support for declarative coding goes much further.  For example, rather than having to use something like an isMemberOf function (for example in Java) to see if some object is an element of a collection, in Python would could do something like:

       
for x in [1, 3, 6]:
       
 

which is clearly much more readable. Or say you want to iterate over both the key and associated value in a dictionary:

       
for key, value in map.items():
       
 

This is much clearer than most programming languages. While it may not be quite as declarative as something like Prolog, it is comes closer to declaring what you are doing than what one typically sees in many languages. There are numerous other examples in Python as well. I also noticed that after a fairly short period of time, I was able to write these constructs from memory more easily than with other languages because I found the notations were easier to remember than the function names (including case) necessary in other languages.

Perhaps an even better example of the declarative style of Python is list comprehensions.   Here is an example:

       
lst = [(a, b) for a in [1, 2, 3] for b in [3, 1, 5] if a != b]
print lst
       
 

which results in "[(1, 3), (1, 5), (2, 3), (2, 1), (2, 5), (3, 1), (3, 5)]" being printed. Compare this to doing this without list comprehensions:

       
lst = []
for a in [1, 2, 3]:
    for b in [3, 1, 5]:
        if a != b:
            lst.append((a, b))
       
 

Packages

 Just as Linux distributions have repositories with many thousands of packages, there are many thousands of packages (over 50,000 according to the pypi site) available for Python. While there are libraries available for Java and C++, there is just not the same kind of availability that you see with Python.  Although many of these may be small and may be of no interest to many, I have already found numerous packages that were needed and helpful, from working with spreadsheets to using ssh2 sessions.  These packages are easy to download and install, and you can start using them immediately. Python includes functions to easily build and install packages, including dependencies.  As I work on using software within a company rather than producing software products for sale (which I did for many years), I don't know whether or not there are issues with licensing using these packages when producing program products written in Python. In my current job that is not a factor.

Productivity

I have seen various estimates of productivity gains using Python versus other languages. For example, I read one comment that said many Python programs were 1/10th the size of their Java equivalents.  I also read somewhere that Python programmers could be many times more productive than in Java or C++. I have to say I was initially skeptical, especially assuming you use similar IDEs.  I used Eclipse for Java and I am am now using PyDev (which is Eclipse based) for most of my Python development. (Because I saw how useful it was, I donated to PyDev even before I got a Python job).  After a matter of months of really heavy use of Python, I have to say I am way more productive in Python.  I'm afraid I don't have metrics, but I know it takes a lot less time to get something developed in Python. Part of this is due to the fact that I have to write a lot less code, but there are many other facets that I have mentioned earlier in this post that also help lead to this result.  But as I said, my evidence is anecdotal.

In the past I worked on program projects with (often too) large numbers of programmers. Most of the time now I work by myself or at most with one other programmer. I do use source code repositories of course. I do a little  work on one rather substantial  project (in terms of function, not code size--this is Python we are talking about).  Although it was written by someone else and I have only a small involvement,  I think it showcases many of Python's strengths. It has just been open-sourced. For certain types of environments (working with lots of Linux servers) it can be a real time-saver, and I am glad the author will get some credit and recognition for what he has done.  (Information about it is available here). But because this is the only example of a fairly large program I have worked on so far, I cannot myself provide insight on creating large programs in Python, nor on how things work out with many programmers working on the project. Obviously others have created such programs and are in a better position to comment.

Conclusion

I have been using Python really heavily for only a matter of months. Although for most of my career I didn't have much choice in what language I used, I do regret I didn't switch to Python for my personal programming projects years ago.  That would have forced me to dive much more deeply into Python much earlier. Although I have read (and continue to read) books on Python, especially on object-orient techniques and doing things in a pythonic way, I feel like I am still just scratching the surface of what is possible.

I haven't been as excited about a programming language for a long time. As much as I have been using it in recent months, if it was just the initial excitement of something new, that would be over by now. The code I write is smaller, more concise, and easier to understand. I have a wealth of existing packages I can pull into my projects.  But beyond those pragmatic reasons, I really like the way the language has evolved, from everything-is-an-object to the many declarative constructs to the decorators and context managers and so on.  I suspect how it evolved to this point is largely due to the fact that has grown up in an Internet/open source environment. I think the "thousand eyes" aspect of open source probably comes into play here.  Coming from my background of mainly statically typed languages (except for specialized languages like Prolog), I'm sure I would have been predisposed to do things more like Java and C++.  I'm glad wiser heads prevailed as Python was updated and enhanced over time.

On thing I wonder about, based on my long-time experience as a programmer, is code quality and when in the project life-cycle bugs are found and eliminated.  In large programming projects you learn that the cost of fixing bugs goes up markedly as you move later in the cycle.  That is, it costs much more to fix them in system test versus earlier test phases, and much more once a product gets to the field.  Obviously, if a compiler catches errors during development that would otherwise be found much later, that is beneficial.  Those kinds of errors are caught by a compiler for a statically-type language.  The questions about this are:
  1. What percentage of these types of issues even exist when you aren't using the same type of restrictive type system, and
  2. Of those that still exist, what percentage get past the development and unit test phases?
Again, I don't have enough experience with Python to really provide an informed answer on these questions.  I think perhaps that the use of dynamic languages like Python increases the importance of unit test cases and using unit test frameworks. I believe the use of such approaches has been shown to significantly improve the quality of statically typed languages as well, but perhaps the importance is higher with dynamically typed languages.  However, there is also the greatly reduced program size (for equivalent function) and much more readable and understandable code that one gets with Python.  I don't think the importance of these facets should be underestimated.  Those that were educated in the same era I was may remember the oft-referenced Miller article about how much information one can remember.  This has always driven me to reduce the complexity of code by limiting how many details you have to understand and remember when using some object, API, etc.. At this point I wonder if the reduced program size, improved clarity, and more powerful functionality (built-in lists, dictionaries, etc.) don't far outweigh the benefits of the statically typed languages?

In summary, I know many will say I am late to the party while others will conclude that I have abandoned my roots (in enterprise programming using statically typed languages) for what they think is little more than a scripting language.  All I know is that I am excited about what I have learned so far, and am looking forward to what more I will discover and what I can produce with Python going forward. Even if for some reason I don't get to continue to use Python over the long term in my professional career, I know I can use it in my personal programming. I have already bought a book on PySide and am excited to write some GUI-based applications in the future.

Friday, May 30, 2014

How I Converted My Chromebook into a Linux Laptop

Introduction

In previous posts I described my experiences with getting Bodhi Linux installed and working on Chromebooks, first a Samsung (ARM based) Chromebook and then a Lenovo Thinkpad X131e (Intel based) Chromebook.  In both cases I was forced to re-use the Chrome OS Linux kernel. Basically I would use the vbutil_kernel utility to repack the kernel used by Chrome OS in one partition, and I would install the Linux distribution (Bodhi in my case) in the next one.  By using the cgpt tool to select which partition was booted, I was able to select Chrome OS or Bodhi for the subsequent boots.

While the approach I described above was usable, it had some limitations:
  • In order to boot a different OS I had to be in developer mode. Thus, on every boot, I got the scary boot screen about OS verification being off (see below). Of course, I only had to type Ctrl-D to get past that screen and boot. However, if I accidentally hit the space bar it would start the process of reinstalling Chrome OS, which would cause me to lose most or all of my data. Although I have never done this, I have read of others who have. I could certainly see the possibility of powering on the machine in the morning, seeing the error message and (not thinking), hitting the space bar.
  • In order to leave space for two Chrome OS versions (when Chrome OS updates it updates the older version in case there is a failure), I lose about half of the SSD space that I could be using for my Linux distribution.
  • Because I was only able to use the Chrome OS kernel on my machine with my Linux distribution, there were issues:
    1. I wasn't able to use the latest Linux kernel available. Also, when I would update my Linux distribution, it would try to update the kernel to no avail.
    2. There were limitations in the kernel provided with Chrome OS. I learned this when I tried to install and use docker.  It was not possible to get docker working because of the way the kernel was compiled.
I should point out a couple of other things about the kernel. For the Samsung Chromebook there aren't a lot of options for kernels to use, because of the ARM architecture. For the Lenovo Thinkpad X131e Chromebook, it is theoretically possible to use vbutil_kernel to sign a different kernel to use. At least that is what I was told. I tried numerous times to do this. Using the verbose verification options of vbutil_kernel, I compared my signed kernel with the Chrome OS one. I could not discern a difference. Yet, I was unable to ever get a non-Chrome OS kernel to boot.  I should point out that, to my knowledge, those who said that a different kernel could be used had not done so with the Lenovo Thinkpad X131e Chromebook, but with other Chromebooks. My particular model is targeted only at the education market. I was only able to get one by getting a refurbished model in the secondary market.  So in terms of getting different Linux distributions running on it, I have (unfortunately) been somewhat of a trailblazer. My searches have failed to uncover much useful information about installing and using Linux distributions on that particular model.

You might be reading this and simply saying, Well you shouldn't have bought a Chromebook then.  I discussed why I bought Chromebooks in previous posts. I am not alone in this thinking. Jeff Hoogland, who heads the Bodhi Linux project (of which I am a part), discussed his thinking on using Chromebooks for Linux desktops here.

Given the limitations I was encountering with Linux on my Thinkpad Chromebook, I had two options to improve the situation:
  1. I could compile my own Chrome OS kernel, including the options I needed. I actually have some experience compiling a Chrome OS kernel in the past.
  2. I could flash a different ROM which allows installing and using any Linux kernel and distribution.
Although option #1 would theoretically be easier, I really don't care any more about being able to boot back into Chrome OS (which option #2 would prevent). I still have a Samsung Chromebook for that if I need it.  Option #2 provides a way to turn my Thinkpad Chromebook into a full-blown Linux desktop.  So, option #2 is the option I took.  As I will describe below, this is not a direction to be taken lightly.

Here by the way is the scary boot screen I was encountering at every boot:

Flashing a New Coreboot ROM

The Lenovo Thinkpad X131e Chromebook uses Coreboot and U-Boot to boot the system. You can find a brief description of it here.  The part that can be overwritten (replaced) is the Coreboot part.  There is a Google+ community which discusses (and in some cases provides ROMs for) Coreboot on Chromebooks here, owned by John Lewis.  John provides ROMs for various Chromebooks. You can find a discussion at his website FAQ page. After I discussed some of the issues I was having trying to utilize other Linux kernels on my Chromebook, he graciously volunteered to try building a Coreboot ROM for my model of Chromebook.

The payload used in the Coreboot ROMs John builds are SeaBIOS, which you can find described here.  These SeaBIOS payloads (that John provides) are great for booting Linux, which is what I am interested in. They may not be useful for Windows.

The tools available for flashing a new ROM are pretty easy to use. However, when considering doing this for a Chromebook there is one possibly significant issue. The Chromebook comes configured with ROM flashing disabled. That is, there is some type of write-protect for the writable ROM, and it is enabled by default. Thus, to flash a new ROM, you must disable this write protection for the ROM writing process.  Each Chromebook model is different. For many, you have to use a jumper across pins to disable the write-protect feature. For the Thinkpad X131e Chromebook, there is a switch.  That is the good news.  The bad news is you have to open up the Thinkpad and remove lots of parts to access that switch.

There is no documentation on the Google Chromebook pages about how to do this. Fortunately, I found the instructions in the hardware guide for the Lenovo Thinkpad X131e Chromebook.  The steps listed in the manual are:
  1. Disconnect the power and remove the battery. Removing the battery is very easy to do on Thinkpads.
  2. Remove the bottom slot cover.
  3. Remove the hard disk or solid-state drive if installed, or the empty hard disk bracket if not. My unit had an empty hard disk bracket.
  4. Remove the keyboard.
  5. Remove the keyboard bezel assembly.
  6. Locate and turn off the write-protect switch.
Steps 1 and 2 are trivial. Step 3 was simple, but I later determined was also unnecessary.  Step 4, removing the keyboard, is a little scary, especially since you have to disconnect a couple of ribbon connectors. These connectors are not like the old ones which plug in. Instead the ribbon is locked into a slot, and it is locked in by a little cover.  Step 5, removing the keyboard bezel assembly was a pain as well.  There was one ribbon connector like the ones I just described, plus one which was a plug-in socket. The plug-in socket was very small and took 15 minutes to unplug. Then to remove the bezel itself was complicated. The manual says to use a plastic pry tool. Once I got things started, I was able to use a plastic knife.

I am sure for people who work on these at a repair facility, these steps are simple. For someone who has not done it, it is not so simple. Although I had removed a Thinkpad keyboard before (different model), the ribbon connectors on this model were smaller and harder to deal with. The keyboard bezel was difficult as well.

Once I had everything removed, flipping the switch (location well described in the manual) was trivial.  Then I had to put things back together. Once I had things back together I tried restarting the system.  It started fine, but I quickly saw the keyboard was not working. So now I had to repeat steps 1 through 4 to remove the keyboard and play with the connectors. This time I skipped step #3. Needless to say after redoing the keyboard connectors and putting the keyboard back on, I did not screw in the keyboard nor put on the bottom plate until I knew the keyboard worked. It took a couple of tries but I got it working. Then I completely put everything back together.

Since I already had Bodhi Linux installed (using the Chrome OS kernel), I booted into Linux. I had previously downloaded the flashrom program provided by John Lewis and fixed the links to the needed libraries as described in his FAQ. 

I first used flashrom to save the existing ROM in case there was something wrong with the new one:

./flashrom -r x131e_chromeos_original.rom

The filename "x131e_chromeos_original.rom" was the name I used for the saved ROM. Next I wrote the ROM John had made available to the flash:

./flashrom -w coreboot-stout-seabios-270514.rom

First, it wrote out some information about the flashrom version. Eventually it said:

Erasing and writing flash chip...

It took several minutes. Then it said it was verifying the flashing and soon after it said verification was successful. So, I rebooted. I should note here that John did not have this model of Chromebook, so to my knowledge, I ws the first to try it.

It booted up without the scary boot screen. Instead, it booted up with a single line describing the SeaBIOS version and that is was created by John Lewis. Then it said to use F12 to select what to boot. I had actually discussed this with John before trying the ROM. Chromebooks do not have F11 and F12 (F12 is the norm for getting to a BIOS screen). He wasn't sure if he had changed it to ESC or F10 or not. He said if not, I could use an external keyboard. Fortunately, I had a USB keyboard.  So I powered off, plugged in the external keyboard. I had already inserted a bootable USB live CD of Bodhi 3.0.0 beta.  I selected the USB drive and tried to boot up the Live CD. It started but hung at the splash screen. As I have seen this type of thing before, I tried again, selecting the USB drive again, but this time I was able to edit the boot line and turn off the splash and specify vga=771. This time the Live CD came up properly.

At this point I decided to install Bodhi. I was able to repartition the main SD card, with some swap space and the rest of the 16GB for my Linux distribution. By doing this, I was erasing the various Chrome OS partitions and my previous Linux install. Upon reboot, I got the SeaBIOS screen and after a second or so of waiting for a possible F12 press, it started my Bodhi distribution. In about 10 seconds I was up and running.

I mentioned the F12 problem to John and he was nice enough to send me a new ROM which used ESC instead of F12. I used flashrom again to write the ROM. With that updated version, I can now use ESC to select the boot device within SeaBIOS.  I made a donation to John because of his efforts.

Running Bodhi on My Thinkpad

I had been running Bodhi beta 3 before (using the Chrome OS kernel), so there were few surprises.  One issue I encountered was that when the screen blanked I could not recover.  However, I then got the latest updates and I noticed a couple of interesting things:
  1. The blanking issue went away. I was able to move the cursor or hit a key and the screen came back as expected.
  2. The look was fantastic! Unlike the previous fairly simple desktop background, there was now an impressive looking desktop background which looks 3 dimensional and has various color gradients.  And the quality of text and icons within various applications looks really sharp.  I don't know if this was due to a theme change or improvements to Enlightenment, but I can say that those who move up from Bodhi 2.4 to Bodhi 3.0 will see a dramatic improvement.
Here is a picture of the desktop:


I think the desktop background is impressive. At the top you can see the shelf. The shelf contains icons for applications as well as widgets for things like the battery, etc..  Similar to Ubuntu Unity, the application icons are used both to start applications and to indicate those that are running. You may be able to see the browser (blue globe) icon with a little orange dot under it to the right side. This indicates an instance is running. If you move the cursor below the icon, it will show you the running instance(s) that you can select.  If you click on the icon itself, it opens a new instance. This is a little different from how the Ubuntu Unity taskbar works. Also, unlike what Ubuntu Unity allows, this shelf can be placed on either side, or top or bottom. You can add additional shelves as well.  Similar to Ubuntu Unity and Mac, you can automatically hide the shelves as well.
 
You also see I have an external drive. The blue dot indicates it is mounted.
 
I initially chose Bodhi because of the Enlightenment desktop environment. With this new version of Bodhi, I am seeing even more enhancements to the Enlightenment desktop environment which makes it unique and more fun and useful.
 
One issue I have encountered is inability to suspend. To be clear, it appears to suspend, but instead of waking up properly, it reboots instead.  I even tried a couple of tpm_tis settings that I used previously to get suspend to work previously. They did not seem to make a difference. Interestingly, I saw that it appeared that the tpm_tis module was being started according to the system log. I tried installing tcsd and trousers which are for trusted computing, and although I was able to get them installed, I still have not gotten suspend to work.  John indicated he thought it was a limitation in the ROM.  I don't know. Having suspend is not a big deal to me anyway since I can boot in about 10 seconds. I normally shut down my machine (rather than suspend) anyway.

Summary

In this post I have described how I turned by Lenovo Thinkpad X131e Chromebook into a full-blown Linux laptop.  I chose the laptop for the hardware (SSD instead of hard drive, enough memory, long battery life) and price. I always intended to run Linux on it.  Although it has been a long and sometimes scary (taking parts of the unit apart to flip a switch) process, I now have the laptop I want booting the Linux distribution I prefer.

Monday, March 3, 2014

How to Install Bodhi Linux on Lenovo Thinkpad X131e Chromebook

Introduction


In previous posts I described what I learned in trying to install Bodhi Linux on the Lenovo Thinkpad X131e Chromebook. In this post, I will describe how to actually download and execute a script to help others get  Bodhi Linux installed on the X131e Chromebook, either on a USB device or on the main SD card.

Please Note First


If you are familiar with the normal operation of Chromebooks, you will note that they automatically update. When you first get your Lenovo Thinkpad X131e Chromebook, if you wish to install Bodhi Linux or another alternative OS, you should first allow your Chromebook to update once, which it should do soon after logging in the first time. The reason I say this is that my Thinkpad Chromebook initially was using a version of Chrome OS with a 3.4.0 Linux kernel, but after updating, it was using a newer Linux 3.8.11 Linux kernel. The newer kernel is preferable.

Instructions Using My Script


Most steps to use in using my script are similar to those I described in an earlier post about installing on a Samsung Chromebook, as well as numerous posts by Jeff Hoogland (Bodhi Linux team leader) about installing on the Samsung Chromebook. There are some differences though that you will need to be aware of:
  1. The instructions for getting into developer mode are different.
  2. The (disk) device mappings are different.  The main SD card on the Thinkpad X131e Chromebook is /dev/sda, not /dev/mmcblk0.
In the following sections, I will describe how to get Bodhi Linux installed on the Thinkpad X131e Chromebook.
Step 1: Boot in Recovery Mode

Keep in mind that you will lose current data, so you should back up any data you need first. Any data you had previously will be lost. I assume at this point that you have a ChromeOS verified boot environment. You now need to boot into recovery mode. The instructions for booting into recovery mode are detailed here. The instructions are different for the Thinkpad X131e Chromebook in that you have to remove power (AC and battery) and then boot into recovery mode within 20 seconds of reapplying power. So after reapplying power, you must quickly boot into recovery mode by  holding ESC (escape) and Refresh (F3) and hitting the power button.

Step 2: Enter Developer Mode


You now should hit Ctrl+D and then on the next screen press enter. You will not be prompted, you have to know to do it. It will ask you to confirm, which you should do. At this point the system should reboot into recovery mode. Once it reboots into recovery mode it takes a few minutes to configure everything, so please be patient.

Step 3: Get into a Virtual Terminal with Internet Access

After the system has started up, select a wireless access point to which to connect. It might take a few seconds after starting up for the list of access points to get populated into the pull-down list.  After selecting the wireless access point, go to the next screen (the google login screen) but do not log in. Hit Ctrl+alt+-> (where -> is where F2 would be on many keyboards).  This will open up a shell window.  For the username enter chronos and just press enter for the password (there is no password).

Step 4: Download and Run the Installation Script

From the command prompt run:

wget --no-check-certificate http://goo.gl/4eyrq4

Now you need to determine which device you are going to install to. Note that there are limitations as to what devices you can use. Although someone must know exactly what and what is not supported, it seems many of us learn  this only via discovery (that is, trial and error).  Based on my various attempts, I think your choices are limited to:
  • A USB flash drive, or
  • The main SD card used as the main drive on the Chromebook
What is clearly known is that only certain USB ports can be used for booting from a flash drive. This has been discovered (and thus documented) for various Chromebook models.  I attempted to install to and then boot from an external SD card and was not successful.  The behavior I saw (a pause and a loud beep) made me believe that booting from an external SD card was not supported. I cannot state this definitively as a fact, but can only recount my own experience.  
 
I have also encountered various USB flash drives from which I could not boot from on either the Samsung or Thinkpad Chromebooks, although I used the exact same steps (scripts) to create the install image as I did for USB flash drives from which I could (always) boot. I spent a lot of time trying to determine why some USB flash drives could be used while others could not. I was unsuccessful. I have not seen the cause, or a way to check for suitability, documented anywhere. So when it comes to USB flash drives, if after installing to one using my script, the boot fails, you may want to consider another USB flas drive. All of the USB flash drives I tried were SanDisk Cruzer drives. A couple worked, a couple did not.  I found no discernable pattern.  An old 4GB and a very new 16 GB worked, while a fairly new 8GB and slightly older 16GB did not.
 
So first you need to decide if you want to install to an external USB flash drive or not. You may want to do this initially, even if your long term goal is to use the main SD card in the Chromebook, just to try out the experience of running Bodhi on the Thinkpad Chromebook. If you choose an external USB flash drive I would recommend at least 8GB, although I have used 4GB for testing previously on my Samsung Chromebook.

You need to know how to identify the device you are installing to.  On my Thinkpad Chromebook I descovered that the main drive (SD card) was /dev/sda.  When I installed a USB flash drive, it appeared as /dev/sdb.  You can use

sudo blkid

to try identify the device you wish to use.  For the main drive (which I saw was /dev/sda), I saw multiple partitions: /dev/sda1, /dev/sda3, /dev/sda5, etc.  For an external USB flash drive, typically you would see fewer partitions. Again, the first USB drive I plugged in was identified as /dev/sdb. The blkid command may show /dev/sdb or /dev/sdb1 and /dev/sdb2, etc.. Given that you know which device to install to, you invoked the downloaded script with the device:


sudo bash 4eyrq4 device

where device is the device to be installed to, which typically would be either /dev/sda (for the main SD drive) or /dev/sdb (for an external USB drive).  After entering the command (which runs the script), you will be provided some information about your Chromebook. To continue, press enter.

Note: The next two steps only apply if you install to the main SD card. Otherwise, the script will run to completion without rebooting or entering more information. You will be given the chance to abort the operation before it writes to your device. Assuming you don't, the filesystem will be downloaded and installed. This takes a while, as it is about 320 MB.

There is one other interesting thing to note about the installation.  A strange thing happened often when I tested the installation script. The extraction and copying from the tgz file to the target drive often encountered issues. It appears that either during the copy itself, or the subsequent unmount command, the machine determined the command was taking too long (perhaps seemed hung) and rebooted. Because of this, I was forced to extract and copy parts of the filesystem image in pieces, and to insert sync commands to insure that previous image parts were completely copied before installing.  So as the script runs, you will notice that it describes extracting sections and syncing and sleeping to try to guarantee success.

Step 5: Choose How Much Space to Give to Bodhi Linux

This step only applies if you are installing to the main drive. The installer will prompt you for how much space you want to give to Bodhi. You should enter an integer amount for how many GB you would like to give Bodhi. On the 16 GB Chromebook the most Jeff (and others) recommended giving to Bodhi is 9 GB (with the maximum being 10).  For information, the initial base Bodhi install occupies around 1.6 GB. Once you select an amount of space to give Bodhi the drive will be re-partitioned automatically and then the system will restart. After restarting,  you will need to go through the Chrome OS setup process once more (see the beginning of step #3). Again, when you get to the google login screen do not log in.

Step 6: Getting the Linux Filesystem Set Up

Again, this step only applies if you are installing to the main drive. Get into the virtual terminal again as described at the end of step #3. Then run the same two commands given in step #4, which will download and execute the script a second time.  This time the installation script will see that your drive has already been partitioned and it will begin downloading the Bodhi Chromebook image which it will then install onto the system. Note that this will take some time depending on your internet connection speed since a 320MB tarball needs to be downloaded and extracted.

After the Script is Finished

Please note that I commented out the line at the bottom of the script which reboots, in case you wish to look around before rebooting.

When you are ready to reboot, enter the command

sudo reboot

After rebooting, you need to enter Ctrl-D if you installed to the main SD card, or Ctrl-U if you installed to the USB flash drive. Note that I discovered only the yellow USB 2.0 USB port on the right hand side of the machine could be used for booting with a USB flash drive. Hopefully you will then boot into the LXDM login screen. Note that the default installed user is bodhi32 and the password is bodhilinux. That user has sudo rights. After logging in, you should then see the Enlightenment desktop.

Please also note that I did run into issues with the wireless dropping its connection sometimes, and the wireless password screen (for the access point I had been logged into) appearing. It seemed rebooting was the only way to resolve this at this point (that is, entering the password did not seem to help).  I have tried various steps to try to solve this issue. My latest attempt was to edit, with sudo, /etc/modprobe.d/ath9k.conf. Note that this file did not initially exist. I put in the line:

options ath9k nohwcrypt=1 blink=1 btcoex_enable=1

Note also that I initially tried only nohwcrypt=1, but this was not sufficient. At time of writing, I have not encountered the dropped connection problem again, but note that I have only had these latest module settings set for about a day.

You will probably want to change the password of the default user.

Future Plans

Fans of Bodhi Linux will note that an alpha version of Bodhi 3.0 was just announced. I may update my script at some point in the future to allow you the choice of installing Bodhi 3.0 instead of the current supported version, 2.4.0.

Monday, February 24, 2014

Installing Bodhi on Lenovo Thinkpad X131e Chromebook

Introduction


In a previous post I explained my decision to choose a Lenovo Thinkpad X131e Chromebook for use as my regular laptop running Bodhi Linux. In this post I will describe the steps I took to install Bodhi on the Thinkpad Chromebook.  In previous posts I discussed using a Samsung Series 3 Chromebook, initially to run the installed Chrome OS, and then to run Bodhi Linux.  Because of my experience installing, and in fact building install tools and images of Bodhi Linux for the Samsung Chromebook I assumed installing Bodhi Linux on the Thinkpad Chromebook would be fairly simple.  Although my previous experience provided me the basic steps, I did run into some issues and surprises.

Existing Documentation


When I wanted to install Linux on the Samsung Chromebook, there were quite a few posts describing how to do so for Bodhi, Ubuntu, Kali, Debian, and other Linux distributions.  I was initially surprised that I could find little or no similar documentation for the Thinkpad Chromebook.  Upon reflection, I realized that this was probably largely due to the fact that Lenovo has targeted the Thinkpad Chromebook at the education market. They in fact offer non-Chromebook versions of the Thinkpad X131e for the mainstream market.  Because the Lenovo Chromebook was not more generally available is a major reason I opted for the Samsung Chromebook in the spring of 2013.

So in looking for alternate OS installation documentation on the Thinkpad X131e Chromebook, not much was available. I did of course find the instructions for entering Developer Mode on the Chromebook, which of course is a prerequisite for attempting any alternative installation. Additionally, I saw references to the fact that the firmware was coreboot. Additionally, there were a few more details on the specific version for the various Chromebooks here.

On newer Chromebooks, like the Acer C720, which have seabios firmware, it is possible to use Ctrl-L (once in developer mode) to boot alternate OS (for example a Bodhi ISO image).  This is called a legacy boot. Alas, the Thinkpad X131e Chromebook apparently does not have that level of firmware.  In fact I was not able to find detailed information on the capabilities of alternate booting using the coreboot stout firmware on the Thinkpad Chromebook. I did not entertain the idea of changing the firmware. I don't know if it is even possible. I did notice on the Lenovo Chromebook that partition 12 on the main disk (SSD) appeared to be boot configuration. Partition 12 (hexadecimal 0xC) was similarly boot configuration on the Samsung Series 3 Chromebook, although the two configurations look markedly different. The partition has the label "EFI-SYSTEM" and has the type "EFI System Partition".

Examining the EFI Partition


The Thinkpad Chromebook partition 12 has two directories: efi and syslinux. Under the efi directory, there is a single directory, boot. It contains 3 files bootia32.efi, bootx64.efi, and grub.cfg. The two efi files appear to be UEFI applications. The grub.cfg file appears to be a GNU grub configuration file. I think it may be a particular type used with EFI.  The grub.cfg file has 5 menu entries, two for local images ("local image A" and "local image B"), two for verified images ("verified image A" and "verified image B") and one labelled "Alternate USB Boot". This last menu entry contains:

linux (hd0,3)/boot/vmlinuz quiet console=tty2 init=/sbin/init boot=local rootwait ro noresume noswap loglevel=1 noinitrd root=/dev/sdb3 i915.modeset=1 cros_efi

According to documentation I have read, this implies that it is booting off of /dev/hda4 (note that the first is 0, not 1).  I believe /dev/hda is mapped to /dev/sda which means we are talking here about booting from /dev/sda4. When I use the cgpt command to examine the partitions on /dev/sda, I see that /dev/sda4 is the Chrome OS kernel with the highest priority. So it appears what is happening here is that it boots the kernel at /dev/sda4 and uses the root filesystem at /dev/sdb3. /dev/sdb is the first USB disk recognized by the system.  The implication is then that it partition 3 on a USB drive recognized as /dev/sdb was a root filesystem and that grub menu entry were chosen, that system would be booted. The grub.cfg file has the default set at 2.  Since grub entries apparently start at 0, that means the third entry ("verified image A") is booting by default. Perhaps if I modified the grub.cfg file to have the default at 4, I could boot off the first USB disk if the root filesystem partition was partition 3. I have not tried making this change.  I don't know if this would work, given that I do not see a grub boot menu even if I hold down the shift key, which I believe would normally show you the boot menu.  Note that when I tried the shift key when booting I was already in developer mode.

The syslinux directory contains several configuration files and what appears to be two kernel files (vmlinuz.A and vmlinuz.B). There is a a README file that states:

Partition 12 contains the active bootloader configuration when booting from a non-Chrome OS BIOS.  EFI BIOSes use /efi/* and legacy BIOSes use this syslinux configuration.

I am not sure how to use this syslinux configuration, given that I believe the Chromebook has by default an EFI BIOS.  Perhaps if I tried rewriting the BIOS I could use this, but I am not considering doing that at this point, even if I could.

After all this investigation, I determine I would try the same approach I had used on the Samsung Chromebook. Perhaps more documentation on the Lenovo Thinkpad X131e BIOS will appear at some point and more options will become obvious.

Note added March 26, 2014: I discovered a discussion of kernel boot modes.  There are 3 boot modes for chromebooks: cros_secure, cros_efi, and cros_legacy. Partition 12 efi directory would be used with cros_efi mode, while the syslinux directory would be used with cros_legacy mode. You can view /proc/cmdline to see which mode is used (it should be at the front). I have seen cros_secure being used on my Thinkpad Chromebook. I don't know how to change the mode. It has been suggested that I would have to install updated firmware to have it change.

Using the Approach I Used on Samsung Chromebook


Given that I couldn't use the legacy boot approach used on newer Chromebooks like the Acer C720 and given that I couldn't figure out how to boot alternatives looking at partition 12 of my Thinkpad Chromebook, I decided to try the same approach I had used with the Samsung Chromebook.  Initially I attempted to boot from a USB drive, as this is easier than modifying the main SDD in the Chromebook. The steps I need can be summarized as follows:
  1. Use parted to create a GPT partition table
  2. Use cgpt create to create/reset the partition table
  3. Use cgpt to create the partition for the kernel (which will be Chrome OS)
  4. Use cgpt to create the partition for the root filesystem
  5. Copy the root filesystem to the second partition
  6. Remove /lib/modules and /lib/firmware in the copied partition and copy them from the Chrome OS filesystem being used. This is necessary because these modules and firmware match the Linux (Chrome OS) kernel to be used.
  7. Repack the running Chrome OS kernel into the first partition, using the needed boot parameters. Note that this step must be executed on the Chromebook being used.
While I had scripts to do things like this already for the Samsung Chromebook, there were differences. The Samsung Chromebook was ARM architecture, while the Lenovo was Intel. Therefore, I needed an x86 or x86_64 filesystem.  While there were Bodhi filesystem images available for ARM (for the Samsung Chromebook) I was not aware of them for Bodhi. Normally, one would boot from a CD or USB drive created from an ISO image.  Without the legacy boot support like the Acer C720 had, this was not an option for me.  Jeff Hoogland, who leads the Bodhi project, suggested that I boot a different Intel based computer from the C720 ISO image he had created (which had the latest packages), and install to a USB drive. Then I could use the filesystem that was generated for my Chromebook.

Dealing with Thinkpad-Specific Issues


Once I had an image to use and an approach, I created a new installation script based on the initial Samsung Chromebook version.  Right away, I had to investigate differences between the Samsung and Chromebook machines, other than the difference in architecture.

First I had to put the machine in Developer Mode.  Each Chromebook is different. On the earliest models there was an actual physical switch. On later models like the Samsung Series 3 Chromebook, there was a key sequence you needed to use when powering on the machine. For the Thinkpad, it was actually more complicated. There was a similar key sequence, but that sequence could only be used after first removing power (both AC and battery), and then use the proper key sequence within 20 seconds after reapplying power (e.g. reinstalling the battery).  The steps are described here. Note that I did not verify whether the 20 second limit is accurate. Secondly, although removing the battery sounds difficult, it is actually quite easy to do on Thinkpads.

Second, I needed to understand how the devices were mapped. On the Samsung the main drive (SDD) is called /dev/mmcblk0.  When I used the blkid command (with sudo) I found that the main drive (SDD) was instead /dev/sda. This meant that instead of making sure not to overwrite the wrong device when creating my boot USB, I had to understand that /dev/sda was the main drive. I also need to know this to locate and repack the running kernel.

The third part I could only learn by experimentation. For some reason, there are limitations as to which USB ports can be used for booting. On the Samsung Series 3 Chromebook there is a USB 2.0 port and a USB 3.0 port. Only the USB 2.0 port can be used for booting. The Lenovo Thinkpad X131e Chromebook has two USB 3.0 ports and one USB 2.0 port. My only successful attempt to boot from USB occurred when I used the USB 2.0 port.  Note that it seems USB 3.0 ports are marked in blue inside, while USB 2.0 ports are not.  On the Thinkpad Chromebook, the USB 2.0 port is on the right side and marked in yellow.

Using the USB drive (one that I had successfully used on the Samsung--some USB drives do not work for some reason), I was able to boot the machine. However, I immediately encountered issues. First the login screen appeared, but clicking on the default userid (I had a default userid and password in the image) did not seem to do anything. However, when I used Ctrl-Alt-F1 to change to virtual terminal 1, and then Ctrl-Alt-F7 to go back to the X display the screen was refreshed. Now I saw the password prompt. I entered the password. It did not appear that I was typing (I should have seen asterisks), but when I hit enter the screen changed. I had actually tried an older Bodhi x86 install image earlier and had seen the same problem. On the older Bodhi image at this point I saw the enlightenment desktop. On this newer image, created with newer graphics packages for the Acer C720, I saw a blank screen.

I saw the version of intel graphics package installed and went searching for a report of issues for this package and adapter. I didn't find a solution right away, but I did find this page about the driver and possible configuration. I decided to try using UXA instead of SNA acceleration. I did this by adding a 20-intel.conf file in /usr/share/X11/xorg.conf.d containing:

Section "Device"
           Identifier "Card0"
           Driver "intel"
           Option "accelMethod" "uxa"
EndSection

I did this by using Ctrl-Alt-F1 to change to virtual terminal 1 and using sudo and vi to add the file.  I rebooted. I again had the problem with the login screen, but once I used the Ctrl-Alt-F1/Ctrl-Alt-F7 sequence to get past that, I now had my enlightenment desktop. Note that with recent open-source video drivers, xorg configuration is typically not needed. Instead, they use kernel modesetting (KMS). Apparently, I needed to add the aforementioned xorg configuration entry to bypass a bug.

Solving the problem with the login screen took much longer. I tried other login managers (the default with the Bodhi install was LXDM), like LightDM, GDM, etc. All had similar problems.  Since the i915 Intel driver was being used, I started searching for issues with that.  I found this Debian bug report, which talked not about a blank screen but about screen corruption. I decided to try adding i915.i915_enable_fbc=0 to the boot parameters. Once I did this (by modifying my installation script to include this boot parameter when repacking the kernel), my login screen issues went away. Note that the setting I added turns off frame buffer compression.

At this point I had what appeared to be a properly working Bodhi Linux system.

One other issue I ran into was that occasionally I would lose my wireless connection. It would ask for the password for my wireless access point that I was previously connected to but re-entering did not help. I could only get it back by rebooting.   In looking around I found that some people using the ath9k driver solved issues by adding /etc/modprobe.d/ath9k.conf (using sudo) with contents:

options ath9k nohwcrypt=1

So far it seems to be working, but it will probably take a few days to know for sure.

Note added March 26, 2014: I continued to have occasional wireless issues even with the above settings.  I recently booted into Chrome OS and updated to the canary channel, which installed a new kernel (still 3.8.11, but dated 3/24/2014). Since then I haven't seen wireless issues, but only time will tell.

Note added March 26, 2014: I recently began experimenting with bodhi 3.0.0 alpha, which is based on Ubuntu 14.04. I ran into a boot issue where there was a long delay at boot time, seeming related to plymouth.  I saw the following error message:

plymouthd: ply-terminal.c: 611 ply_terminal_open: Assertion `terminal != ((void *)0)' failed.

After a long period of time (sometimes 2 minutes), the login screen would appear. To work around this issue, I added (as sudo) the file /etc/initramfs.conf/conf.d/splash and added the line:

FRAMEBUFFER=y

and then executed the command (as root):

update-initramfs -k 3.8.11 -u

and rebooted. I am still seeing the error message, but I no longer have the long delay. I found the above setting related to some plymouth bug reports. Only time will tell if it resolves the issue, as it did not happen every time.


Comparing Bodhi on Thinkpad Chromebook vs. Samsung


In my previous post, I explained reasons for moving to the Thinkpad Chromebook for Bodhi and away from the Samsung. The main reason was of course the ARM architecture.  I will list here a few of the advantages:
  • Audio worked immediately, without having to investigate special settings. The main speaker was muted when I used the headphone jack, something I never got working on the Samsung (using Linux).
  • The backlight control worked immediately, without having to investigate special settings.
  • The backlight turned off when the screen blanked (unlike the Samsung which didn't)
  • I was able to use Ctrl-Alt-F1 to switch to virtual terminal 1 and Ctrl-Alt-F7 to switch back to the graphical screen. On the Samsung I could never switch back.
  • Booting is faster. While the boot time (from the main SDD) on the Samsung was about 10 seconds, on the Thinkpad it is even faster.
  • On the Samsung sometimes when I would boot the cursor would be frozen. I would have to power off and start again. I haven't encountered this on the Thinkpad.
  • The filesystem for Bodhi on the Samsung (ARM) Chromebook is based on debian, while for x86 machines the filesystem is based on Ubuntu. Yes, Ubuntu is based on Debian, but the x86 filesystem uses Ubuntu packages and the Ubuntu repository.  This means things like the Bodhi Linux AppCenter work properly, since such things assume Ubuntu repositories and packages, not Debian.

Summary


In this post I have described my experience in determining how to get Bodhi Linux installed on a Lenovo Thinkpad X131e Chromebook. I can say at this point I much happier with the state of Bodhi on my Thinkpad Chromebook than the Samsung, despite the months of effort I spent on improving the experience on the Samsung. In a future post, I will describe how to access and use my installation script to install Bodhi Linux on the Thinkpad Chromebook.

Wednesday, February 19, 2014

Why I Switched from Samsung to a Lenovo Chromebook

Background

In earlier posts I described my decision to get a Samsung Series 3 Chromebook and my experience with using Chome OS. Initially, I installed crouton so that I could run various Linux programs and do more locally on the machine rather than always being connected. Subsequently, I decided to move away from using Chrome OS and install the Bodhi Linux distribution. I should point out that my decision was not based on any problems with Chome OS per se, but due to a feeling of discomfort I felt with being tied to the cloud, especially with reports of hacking and interception by criminal, government, and corporate entities. I explained my decision in some detail here.

While I was able to get Bodhi working on the Chromebook, it was not without considerable effort. About the time I decided to install Bodhi, the Bodhi project leader decided to drop official support for the ARM architecture packages for the Samsung Chromebook.  As I will explain later, I believe that decision was both understandable, and ultimately, reasonable.

Making Bodhi Work Well on the Samsung Chromebook


When I was told that official Bodhi support for ARM was being dropped, I volunteered to help build and maintain Bodhi (and enlightenment) packages for the Samsung Chromebook.  Thus began an interesting adventure.  While I had used Linux both personally and at work for well over 10 years, I had not done some of the tasks I undertook in trying to provide a first-class Bodhi experience on the Samsung Chromebook. Things I ended up doing included:
  • Building large numbers of debian packages using debian build tools like dh_make.
  • Building a new Chrome OS kernel following instructions I found here. While I was successful in building the kernel and getting Mali graphics working properly, the kernel itself proved unstable.
  • Creating chroot environments containing debian packages for building debian packages for ARM and also for creating a root filesystem for the Chromebook.
  • Writing scripts to install Bodhi on the Samsung Chromebook. These scripts, built from other existing scripts (written by others), handled various tasks, including:
    • Using cgpt to update and add Chromebook partitions.
    • Using vbutil_kernel to pack (or repack) Chrome OS kernels into a kernel partition.
    • Extracting and tweaking the Bodhi root filesystem.
While the experience was rewarding in that I learned new skills and tricks, it was also quite frustrating.  The main issue of course was the fact that the Samsung Chromebook used an ARM processor instead of Intel or AMD. While thousands of packages are widely available for Intel and AMD (32 and 64 bit architectures), less are available for ARM. Part of the reason for this is there are various flavors of ARM. Thus packages built for the Chromebook might not work on other things like a Raspberry Pi board.  Because of this, you might find packages built for ARM that don't work on your particular ARM-based computer.  

As I explained in previous blog posts, I often ended up building packages myself for ARM. However, in some cases the source was not available (that is, it was closed source) or I was unable to successfully build all the dependencies. In addition, even when I got things built and installed for the Samsung Chromebook (after months of effort), I still ran into various issues:
  • While I could get audio working, it was difficult. Additionally, while I was able to control the volume of the speakers and headphones separately, it did not seem possible to have the speakers automatically mute when plugging into the headphone jack.
  • While I was able to control the backlight brightness via scripts, when the screen blanked, the backlight stayed on.
  • The trackpad drove me crazy. To be clear, I used Thinkpads (first IBM and later Lenovo) for years with the trackpoint.  So using a trackpad was new to me. I don't know if the fact I learned touch typing was an issue, but on the Samsung Chromebook (which has a big trackpad) I constantly accidentally hit the trackpad.  This caused various unwanted results.  Additionally, I had trouble with the sensitivity. Double clicking using gestures was difficult while accidental single clicks were a common occurrence.  I used the syndaemon program to help as well as adding options to xorg configuration.  Nevertheless, I was never satisfied with the results. Additionally, I can handle the trackpoint easier and with none of these issues.
  • Any usable experience with browsing must support Flash.  Flash support is generally not available for ARM.  The only support is for the Google Chrome (Chromium) browser and Google's Pepper Flash support.  Initially I was able to find an early version (version 22) of Chromium which had been built for raspbian, which is an operating system for the Raspberry PI computer (also ARM based).   Eventually, I was able to build a Bodhi filesystem based on debian jessie (testing) and, with a lot of investigation, install a newer (version 31) of the Chromium browser from Ubuntu. So although Chromium is a good browser that one might choose anyway, the ARM architecture essentially limited me to only Chromium because it was the only way to get Flash support.  
  • I was never able to figure out how to get google hangouts to work, even though I was using the Chromium browser. I tried copying various libraries from Chrome OS but was never able to get it to work. Skype, which is proprietary, was not an option as there are no ARM packages available.
Despite the issues I was able to create a quite usable system and use it daily. It booted up in less than 10 seconds and I was able to do my daily tasks. Over time however, I was continually frustrated by the experience (especially with the trackpad) and the knowledge that I was continually going to encounter limitations because of the ARM architecture.  I had eventually come to the same conclusion as had Jeff Hoogland, the Bodhi Linux project leader. The issues with working with ARM architecture were not worth the effort. In Jeff's case, he was initially trying to support various ARM platforms, including the Raspberry Pi.  But even for the Samsung Chromebook, it turned out, to me anyway, just not worth the effort.
 
I should point out that there is quite a lot of support for ARM on Linux. Many Debian and Ubuntu packages are built for ARM.  Additionally, Linaro does excellent work in providing open source Linux support for ARM. You will also find tutorials on installing various types of Linux (Ubuntu, Debian, Arch, Kali, etc.) on ARM, especially Samsung Chromebooks.  And David Schneider at Google has done excellent working with crouton, providing an easy way to set up a chroot environment to run Linux under Chrome OS on Chromebooks, including the Samsung Chromebook. Despite this, there is just not near the level of support for Linux for such devices as there is for Intel and AMD based devices.


Lenovo Thinkpad X131e Versus Samsung Chromebook

In comparing the Lenovo Thinkpad Chromebook to the Samsung Chromebook, two things stand out as most important to me:
  1. The Thinkpad Chromebook is built on Intel architecture, not ARM
  2. The Thinkpad Chromebook has the trackpoint
However, if someone reading this is considering acquiring a Chromebook, it is important to understand the following:
  • The point about the trackpoint is  a personal preference. If you are familiar with and comfortable with trackpads, this may not be an issue for you at all. In fact, if you are not familiar with the trackpoint you may not even like it. Of course, the Lenovo Chromebook has a trackpad as well, but it is considerably smaller than the one on the Samsung Chromebook.
  • If you plan to use the Chromebook for its intended purpose many of the issues I have mentioned would have no impact on you. That is, if you use the Chromebook solely to run Chrome OS the Samsung Chromebook may be quite satisfactory.  Although I assume the Google team probably has more issues building and supporting Chrome OS on ARM as compared to Intel, that should be largely transparent to the user.  I would note however that while the version of the Chrome OS for Intel (x86) Chromebooks appears to be at 3.8.11 (at least mine upgraded to that), I believe the Samsung Chromebook version is still at 3.4.0.
In terms of using the Chromebook solely for Chrome OS (i.e. not running some Linux distribution in addition), the machines are comparable.  The main advantages of the Samsung Chromebook in my opinion would be:
  • The Samsung Chromebook is lighter by at least a pound
  • The Samsung Chromebook is thinner
  • The Samsung Chromebook looks better. It looks thin and sleek when compared to the Lenovo
  • The Samsung Chromebook has a bigger trackpad, if this is something you use and prefer
  • The Samsung Chromebook has no fan, making it quiet.
  • The Samsung Chromebook lists at only $249 and is readily available, while the Thinkpad Chromebook lists at $399 is supposedly available only to the education market. (You can find it on a secondary market however).
The main advantages I see of the Lenovo for use solely for Chrome OS are:
  • The Thinkpad Chromeobook has 4GB of memory instead of 2GB. Although this often is not important, in terms of having many tabs open in the browser and running graphics intensive applications, this can make a difference.
  • The Thinkpad Chromebook is more rugged. It has rubberized edges and corners, reinforced hinges, and a spill-proof keyboard.  It was designed for potentially rougher treatment in schools.
  • The Thinkpad Chromebook has a removable (and replaceable) battery.  If you are familiar with Thinkpads, this is a feature they have had for a long time.
  • The Thinkpad Chromebook has the trackpoint. This is an advantage if you like this as opposed to (or in addition to) a trackpad.
  • The Thinkpad Chromebook has an ethernet port. For the Samsung Chromebook you would need a separate adapter (fairly cheap) and driver support.  I don't think the 3.4.0 Chrome OS kernel has support for some of the USB ethernet adapters, but I am not sure. I actually had to build the driver for the Chrome OS 3.4.0 kernel (for the Samsung) myself to use the adapter I purchased.
  • The Thinkpad Chromebook has 2 USB 2.0 ports and 1 USB 3.0 port.   
  • The Thinkpad Chromebook has a VGA port.
 
You can see a video comparing the Samsung Series 3 and Lenovo Chromebooks here. You can see a video comparing the Samsung Series 5 and Lenovo Chromebooks here.
 
For use as a Linux computer, and not solely for Chrome OS, there is no comparison, at least in my opinion. An x86-based Chromebook is far preferable to one based on ARM for the reasons I discussed earlier. 
 
Although I have discussed the Lenovo Thinkpad Chromebook, of course it is not the only x86 Chromebook available. And there are new ones coming out frequently. One interesting option is the Acer C720.  The 16GB model lists at $199 and the battery life is outstanding (8.5 hours).  Additionally, unlike earlier Chromebooks like the Samsung and the Lenovo Thinkpad, you can boot a Linux on a USB drive directly, making it easier to run Linux as an alternative to Chrome OS.  Quite frankly, if you are not partial to Thinkpads (and the trackpoint), the new Acer model is a very attractive choice.

Why Choose a Chromebook at All?

A reader might wonder why, if I want to use Linux, would I buy a Chromebook? After all, you have to put the Chromebook in developer mode, bypassing the verified boot mode just to use Linux. This means every time you boot you encounter the scary "Chrome OS verification is turned off" screen and have to hit Ctrl-D or Ctrl-U to boot.  Well, there are a few reasons:
  • Chromebooks are cheaper. I assume this is partially due to the fact that you don't have to pay the Windows tax due to the bundling of Microsoft Windows.
  • The Chromebooks have an SSD instead of a hard drive. While you can buy other laptops with SSD, they are typically larger SSDs and more expensive.
  • They tend to have long battery life. I think a laptop needs good battery life or you are often worrying about having to plug back in.  I see many laptops advertised with 3 or 4 hour battery life. That doesn't seem to be enough to me.
  • I like the option of being able to boot Chrome OS. I don't dislike Chrome OS. It certainly makes sense for various environments, including education markets and for businesses who want a more locked-down desktop.  It also is a reasonable choice for someone who has almost constant network connectivity and is willing to work almost entirely in the cloud using a lot of Google services.
Clearly, a Chromebook is not for everyone, especially if you want to run an alternative operating system like Linux.  And the Chromebooks tend to have lower end processors. If you want a power machine, for intense graphics and/or gaming for instance, you probably want to look at something else. But for what I use my computer for, they offer the right balance of price, weight, battery life, and size for me.

Installing Bodhi Linux on the X131e Chromebook

Given the experience I had installing Bodhi Linux on the Samsung Chromebook, you would think it would be simple for me to do the same on the Thinkpad Chromebook. That did not turn out to be the case, but after some effort and investigation, I have successfully done so. I have also created a script so others can do the same if they wish to do so. I will discuss all this more in an upcoming post.