Migrating Eclipse Workbench from Windows to Linux

Some folks like to have a relationship among the development tools they use regardless of the underlying operating system. I would certainly qualify as one such individual, because I honestly don’t believe that the operating system should adversely impact the development environment of choice. Of course, there are specific exceptions to the rule: .NET development is a little painful in Mono Develop, and most people writing .NET applications are very likely going to be working under Visual Studio.

My desire to maintain such similarity first started with ActiveState’s Komodo which I once used for PHP and Python development. It amused me to no end that I could purchase one license and use the same IDE under Windows and Linux with virtually no difference in environment. (There was a bug with the color picker under X windows when it came to syntax highlighting and other preferences, but there was a really cheesy solution. More on that in a moment.) The best part, too, was that I could copy my configurations from Windows over to my .komodo directory, change the file and directory paths in one or two XML configs, and it’d work just like it did in Windows. Remember that little bug I mentioned in the parenthetical about colors? By setting your color preferences under Windows and copying the configurations over it was possible to work around such limitations. It was a kludge but it worked.

Eclipse is a whole ‘nother animal. Although it’s slightly more well-behaved with Galileo (Eclipse 3.5), it’s nearly impossible to copy a workspace from Windows to Linux and expect anything to work. However, if you’d like to maintain your open projects and files, I have just the solution. Some fair warning is in order: This little tip does take some effort to complete, and I’d highly recommend having access to Python (Ruby would work just as well). Unless you know a great deal more about Eclipse internals than I do, the extra effort of guessing data types isn’t really worth it.

Let’s get started

The first and step is to copy the desired workspace from your Windows install over to your home directory. In my case, I did something like this:

[gridlock-ix:~]$ cp -a /mnt/windows/d/projects/eclipse/workspace .

You can also use your favorite file system browser. If you do, make certain the .metadata directory has been copied as well because it won’t be visible by default. For Konqueror (and probably Dolphin, Nautilus, and others), you’ll need to verify “Show hidden files” is enabled:

Konqueror Screenshot

“That’s it?”

No, not quite. That was the easy step. Don’t worry, it gets much more interesting (and fun)!

Since you probably have a significant number of open files if you’ve been using Eclipse for any length of time–after all, any good developer should have a plethora of things open “just in case”–you’ll want to preserve the behavior of your previous Eclipse installation as best as possible during the import. As such, you’ll also need to modify some paths since the ridiculous notion of drive letters will be left behind (thank goodness). To do so, open the workbench configuration file located at ~/workspace/.metadata/.plugins/org.eclipse.ui.workbench/workbench.xml.

Using your favorite editor, examine the file and look for any path attribute that appears to point to a DOS-style drive letter, such as d:/eclipse. You then have two choices but both involve replacing the drive letter with the absolute path as it would exist under your current OS. If you were working on the project locally, you can either elect to copy it to your home or you could mount the Windows partition (preferably with ntfs-3g) and operate on it directly. Obviously, this particular choice will impact the exact path you’ll be injecting into the configuration. In my case, the project root was no longer d:\ as my “D” partition is mounted under Gentoo as /mnt/windows/d/. What can I say? I’m original.

It’s probably worthwhile to mention that this little trick is applicable only to projects that have been opened from a DOS-style path. It won’t work for files opened via an SSH plugin, for example. Mounted network drives will work, too, and I’d imagine network paths shouldn’t require much more effort. Do, however, verify that the new path exists before you continue. If you don’t, Eclipse will whine. Loudly.

Once you’ve verified that the paths exist in their latest incantation (you did do that, didn’t you?) you should have something that looks vaguely like the following:

workbench.xml: Finished!

Absolute path names might be overkill, but be mindful that one of the most important tenants in the Unix world is “if it doesn’t work when you use a relative path, you should have included that darned leading slash and written it in absolute form.” (I made that up.)

At first blush, it would seem that this is all that’s needed. It isn’t. If you don’t believe me, try opening Eclipse. You’ll probably receive something about the resource not existing, and if you right-click on your project (from the project explorer) and open “properties,” you’ll notice that the project is still listed in its old location. In my case, this would be “D:/projects/python.”

What’s the deal? Well, it’s unfortunate, but in Eclipse, the .xml configuration files exist only to make you feel better–as if you’re in control. It’s like picking nitrous over deep sedation when you have your wisdom teeth pulled. The net effect for the dentist doesn’t matter much–you’re still out of it. It’s just that with the nitrous, you feel you’re in control of your destiny. I’m awake! By golly, they’ll never take me for a–whoa, where’d these holes in my mouth come from?

XML configs in Eclipse are like nitrous. They cover pretty well for what effectively amounts to major surgery.

Fixing the project location

Now that you’ve gotten the file paths fixed under workbench.xml you’re going to need to fix the project path. Fixing the project paths will require a little handcrafted goodness, but since we’ll be using Python to do the dirty work, it’s almost effortless. If you don’t have Python, you should download it and follow along. We’ll pretend it’s like those annoying sing-a-long books. Well, without the singing.

First, a little note about Eclipse. The on-disk repository for configuration data is a mess. If you’re reading this far, chances are you’ve probably discovered that on your own. If not, you’re about to. Let’s take a look.

Open a shell and change to your ~/workspace/.metadata/.plugins/org.eclipse.core.resources/.projects directory. Yep, you read that right. Type in the whole thing. Now, if you do an ls you’ll see a listing of every project you’ve configured under your previous Eclipse install. In my case, I have exactly three because I haven’t yet imported the other 20+ projects from when I upgraded to Galileo:

[gridlock-ix:.projects]$ pwd
[gridlock-ix:.projects]$ ls
Chatly  Cited  FSUtils

Now, you’ll need to do each of the following steps for all of your projects. I’m lazy, so I may write a script to cover for you in the future if you’ve an aversion to manual labor like I do. Just remember: It’s always wise to do things yourself so you have an intimate understanding of how–and more importantly why–it works as it does. Don’t worry, we’re only going to change exactly one file per project. That’s it–I’m not kidding.

Next, pick a project, change to the directory, and type ls -al so you can see a nice listing of everything. In my case, I picked my Chatly project. Here’s what it looks like:

[gridlock-ix:Chatly]$ ls -al
total 24
drwxr-xr-x 5 bshelton bshelton 4096 2009-07-05 19:54 .
drwxr-xr-x 5 bshelton bshelton 4096 2009-06-29 16:56 ..
drwxr-xr-x 3 bshelton bshelton 4096 2009-06-28 18:15 com.python.pydev.analysis
drwxr-xr-x 4 bshelton bshelton 4096 2009-07-03 15:11 .indexes
-rwxrwxr-x 1 bshelton bshelton   74 2009-06-28 14:45 .location
drwxr-xr-x 2 bshelton bshelton 4096 2009-07-05 15:48 org.python.pydev

See the .location file? That single entity is the bane of our conversion process and provides Eclipse with both the project path and the project root for every single file you couldn’t open. I wonder what it contains?

[gridlock-ix:Chatly]$ less .location

00000000  40 b1 8b 81 23 bc 00 14  1a 25 96 e7 a3 93 be 1e  |@...#....%......|
00000010  00 24 55 52 49 2f 2f 66  69 6c 65 3a 2f 44 3a 2f  |.$URI//file:/D:/|
00000020  70 72 6f 6a 65 63 74 73  2f 70 79 74 68 6f 6e 2f  |projects/python/|
00000030  43 68 61 74 6c 79 00 00  00 00 c0 58 fb f3 23 bc  |Chatly.....X..#.|
00000040  00 14 1a 51 f3 8c 7b bb  77 c6                    |...Q..{.w.|

Ah hah! It’s a beautiful “eureka” moment, isn’t it? But what’s all that other cruft? To answer that question, let’s examine .location from Python.

[gridlock-ix:Chatly]$ python
Python 2.5.4 (r254, Jul  5 2009, 22:04:12)
[GCC 4.1.2 (Gentoo 4.1.2 p1.0.1)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> s = open(".location", "r").read()
>>> s

The magnificent thing about Python is that it prints non-printable bytes in a manner that’s easily to read. Each “\xNN” group is a single byte that cannot be expressed with printable characters. Likewise, the interspersed characters that appear between each byte–such as the “@” and “#” near the beginning–appear because they can be represented by the integer values 64 and 35, respectively. Obviously, this is a gross perversion and oversimplification of bytes and byte processing in Python, but it’s pretty close.

I’ll come clean up to this point. I haven’t any idea what any of these bytes mean, and I certainly had no idea last night when I came up with the notion of posting this! What I do know, particularly when I encountered this yesterday, is that it looks quite similar to a serialized data stream typical of Java. If this were the case, then it’d have to contain additional information describing the project path. What I mean by this is best illustrated by an example: Take the string “URI//file:/D:/projects/python/Chatly” for instance (the leading $ doesn’t count because that’s an artifact of Python’s rendering of individual bytes from the interactive interpreter); it has a length of 36:

>>> len("URI//file:/D:/projects/python/Chatly")

If we were to serialize this in a binary form, the best possible way to keep track of 36 individual bytes (representing ASCII–let’s ignore Unicode for the time being) would be to encode the length of the string somewhere. Since we’re operating on a hunch that this is what Eclipse is doing, let’s see what the number 36 gets written as if we use a short integer:

>>> import struct
>>> struct.pack("h", 36)

Hmm. Recall that Python displayed the applicable portion of our .location file as \xbe\x1e\x00$URI//file:/D:/projects/. What’s the deal?

This, friends, is what CS types refer to as byte-order. Since the Python interpreter running from my system (Intel-based) is using “little Endian” byte ordering, the least significant bit is displayed first. What this means to you (since we’re only interested in fixing .location) is that a simple reversal of the bytes will give us the correct length. Let’s force struct to use big Endian byte order:

>>> struct.pack(">h", 36)

Ah, there’s our number. So how do we fix this?

In Python, we can cheat. The easiest way is to do the following. First, determine the length of the string containing whatever path you have to your project repository:

>>> len("URI//file://mnt/windows/d/projects/python/Chatly")

Next, determine what the new value will be (using big Endian byte ordering):

>>> struct.pack(">h", 48)

And then we implement our “cheat.” Since we can manipulate bytes from the Python interactive interpreter directly using little more than the keys available on any standard US keyboard (with apologies to non-English-speaking foreigners), we’ll change the value of the string and the first short int preceding it:

>>> s = '@\xb1\x8b\x81#\xbc\x00\x14\x1a%\x96\xe7\xa3\x93\xbe\x1e\x000URI//file://mnt/windows/d/projects/python/Chatly\x00\x00\x00\x00\xc0X\xfb\xf3#\xbc\x00\x14\x1aQ\xf3\x8c{\xbbw\xc6'
>>> s

Then we’ll write it to disk:

>>> open(".location", "w").write(s)

If we examine the file now, we’ll notice that several bytes have change:

[gridlock-ix:Chatly]$ less .location

00000000  40 b1 8b 81 23 bc 00 14  1a 25 96 e7 a3 93 be 1e  |@...#....%......|
00000010  00 30 55 52 49 2f 2f 66  69 6c 65 3a 2f 2f 6d 6e  |.0URI//file://mn|
00000020  74 2f 77 69 6e 64 6f 77  73 2f 64 2f 70 72 6f 6a  |t/windows/d/proj|
00000030  65 63 74 73 2f 70 79 74  68 6f 6e 2f 43 68 61 74  |ects/python/Chat|
00000040  6c 79 00 00 00 00 c0 58  fb f3 23 bc 00 14 1a 51  |ly.....X..#....Q|
00000050  f3 8c 7b bb 77 c6                                 |..{.w.|

But guess what the real benefit is? If we open Eclipse at this point, it’ll load everything that was previously opened under Windows–if this was the source project. You’ll want to repeat the steps above for any other projects that contain opened files, too. Here’s a couple of screen shots to prove it:

Eclipse is working!

Examining the project path yields:

Project path is correct!

Now, there is one significant issue with importing Eclipse settings in this manner. If you refer to the screenshot of the entire Eclipse IDE (image before last), you’ll see that I’ve highlighted in red the lack of a correct interpreter. That’s because Eclipse is still pointing to the interpreter path from Windows. The change is really easy, thankfully. Simply go to Window -> Preferences and fix the path under PyDev:

Fixed PyDev path

Once Eclipse scans your Python library, most auto-complete functions will be back in working order. The only thing I haven’t yet fixed is adding PyQt to PyDev’s autocomplete, but I stopped working on that as it was getting late last night! I expect it’s a simple oversight related to how I installed PyQt in the first place.

Don’t forget

If this becomes a significant issue, which I expect it might, I’ll be writing a script to import settings from Windows so that you needn’t worry about fixing each project .location manually. For a handful of projects, the fix is fine. For dozens of projects, it gets much worse!


Leave a comment

Valid tags: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>