Friday, April 4, 2014

Little rant about some runaway pharo refactorings

I'm still trying to explore different ways to play with MC/git/filetree on the pharo-vm substrat.

But there's a serious hurdle, it's named Pharo2.0 image, and unfortunately the VM is produced by this brand.

IMO, it's the worst brand ever. I much much prefer Pharo3.0.

Pharo2.0 tools are constantly getting on my way, here is a small example of a disastrous change.

I have merged several branches directly with git in a batch, I know it won't managed MC meta data, but I want  to see how it goes.
In parallel, I have merged the MC version from package-cache at image side.
When I try to save my merged package on filetree with MC, there is a wait dialog opening but not progressing.
 

Hmm let's interrupt with CMD+SHIFT+. the process is blocked in resultSemaphore wait 
MCRepositoryGroup>>includesVersionNamed: aString
    " check for existing version name in parallel over all repositories "
    | resultSemaphore results |
    resultSemaphore := Semaphore new.
    results := Array new: self repositories size.
   
    self repositories doWithIndex: [:repository :index |
        " fork of test for each repository "
        [[ results
            at: index
            put: (repository includesVersionNamed: aString)
        ] ensure: [ resultSemaphore signal ]] fork ].

    " wait for all requests to finish "
    self repositories size timesRepeat: [ resultSemaphore wait ].
    " check if any repository included the given versionName already"
    ^ results anySatisfy: [:result| result = true ]  
 


Huh? The purpose of anySatisfy: is to exit the loop at first match.
Here we will scan all the repositories, in parallel (well there is a single native thread so far, but this can eventually avoid some long network interrupts if only I/O are non blocking).
Alas, in all case, it will scan all repositories, a tremendous work defeating the purpose of anySatisfy:. Not even thinking what will happen if one is unreachable?
Though, it can make an interesting subject for a future post: how to implement a real parallel version of anySatisfy: that is to launch several processes, stop them at this first positive hit and return?

But there is worse than this unprogressive dialog, since I broke a filetree monticello.meta/version via incorrect git merge, a debugger wanted to open in background, but didn't until I manually interrupted the wait. Ah great tools.

Now let's see how it was cleaned up in Pharo 3.0:

includesVersionNamed: aString
    " check for existing version name in parallel over all repositories "
    | results |

    results := Array new: self repositories size.
   
    self repositories doWithIndex: [:repository :index |
        " fork of test for each repository "
        results
            at: index
            put: (repository includesVersionNamed: aString) ].

    " check if any repository included the given versionName already"
    ^ results anySatisfy: [:result| result = true ] 


Good to sea there is no more fork but in the comments! For sure Pharo code is better... than Pharo.
But don't you see several smells remaining?

In parallel, take a look at Squeak method:

includesVersionNamed: aString
    ^ repositories anySatisfy: [ : each | [each includesVersionNamed: aString] on: Error do: [false]]



OK, OK, Pharo has a license to break things when they are too much entangled. It's not my favourite strategy, but it can reward faster, especially when starting from Squeak.
But in this particular case I fail to see how. Bad code is bad code.

OK, OK, It's easy to pick one wrong fix and criticize.
But the problem is that 2.0 tools are so unusable that I'm quite sure that such wrong refactorings are not isolated.

Squeak is plenty of dust and has room for improvment, no doubt about that.
But it took many years to accumulate all that mud in Squeak, I would say.
If Pharo is accumulating its own at this pace, waouh, I better understand now how the number of closed issues can grow exponentially ;)

Hey, it's a joke, I don't want to ruin the moral of Stephane and Marcus, so I'll mitigate: I see plenty of positive refactorings coming with 3.0, and I wish Pharo will succeed in making a better Squeak. So please enforce the reviews!
Count this post as my contribution of the day to the review process.

Thursday, April 3, 2014

Compiling pharo-vm on SnowLeopard (OSX 10.6)

As indicated on the pharo-vm page, apart Xcode, you also need git, wget, cmake, gnu tar with xz (for uncompressing cairo librairy). There are simple instructions to install these. If you're not too special, follow them. If you like sport, read this.

My OS/hardware is behind, my disks are all crowded, I want to control what is installed. I decided to keep my ancient Xcode version (3.2.6), install from dmg packages what could be (cmake, git - BEWARE, not a recent git, 1.7.12 will do see previous post) and the remainder via source code (wget, tar, xz).

There is no special difficulty, except that wget must be configured with
$ ./configure --with-ssl=openssl
$ make
$ sudo make install

If some documentation fails to generate (gettext?), never mind, proceed.
One must also arrange to have gnu tar taking precedence over, or replacing default tar, I omit the details here, I'll come back to it.

After cloning the pharo-vm repository (see previous post), let's produce a VM.
$ cd image && ./newImage.sh
$ ./pharo generator.image eval 'PharoVMBuilder buildMacOSX32'

Bad news, with such configuration, the automated procedure does not work for me.

0. Starting the image interactively

This is required for debugging, but provided ./pharo command seems to allways start headless.
I have too many different VM around for the normal Mac GUI to be efficient. Since I may restart many times, and since git will force me to use a shell anyway, a command line is perfect, let's pick some 2.0 VM installed somewhere else:
$ ~/Smalltalk/Squeak/pharo/Pharo.app/Contents/MacOS/Pharo generator.image

Now let's jump the hurdles...

1. Generated source does not compile

Many compilation errors... the include files are not found!
Haha, the path to SDK provided thru -isysroot option is not that of my machine...
My fault, I did not want the latest Xcode, but why should I?

Good Point: this is not in an obscure configuration file hidden somewhere on the disk, but a method directly accessible from the generator.image. It can be discovered by extended search / literal string with it: 'Xcode' or 'sysroot' lead to it.

Solution: I had to change 
CogFamilyCocoaIOSConfig>>setGlobalOptionsAfterDetermineSystem:  maker
    maker set: 'CMAKE_OSX_DEPLOYMENT_TARGET' to: '10.5'.
    maker
        set: 'CMAKE_OSX_SYSROOT'
        to: '/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX', self sdkVersion, '.sdk'

to
        to: '/Developer/SDKs/MacOSX', self sdkVersion, '.sdk'


Bad point, I'm not on the automated route anymore, before going back to automation, I must patch one of the MC packages with something that appear as very platform/installation specific... Then I'll have to constantly care about what i stage, commit and push back on github.
The right solution could be to automatically discover the path, maybe with some heuristics. Isn't it some sort of cmake base service?

While at it, I also changed cairo rules to use /usr/local/bin/tar for my specific installation:
CMCairo>> unpack
    gen set: #unpackTarget toString: '${libSourcesDir}/touch.cmake'.
    gen puts:
'add_custom_command(OUTPUT "${unpackTarget}"
        COMMAND /usr/local/bin/tar -Jxf "${archiveFileName}"
        COMMAND touch "${unpackTarget}"
        COMMENT "Unpacking ${libName} ... "
    )

'.

2. Generated CMakeLists.txt has syntax error

Huh??? This did not occur at first attempt...
Haha, the syntax errors are near the end of file...
It reminds me something... Ah yes, read the comment of StandardFileStream>>padToEndWith:
The files have been overwritten but not truncated!
Bad luck, the path to my SDK was shorter than original...

Solution: remove the build dir content and restart build.
$ rm -rf build/*
$ ./pharo generator.image eval 'PharoVMBuilder buildMacOSX32'

Quite large a hammer, this could certainly be more selective.
Anyway, this is an error prone manual operation, and should be in the automated procedure. Or the truncation story should better be fixed either in image or in VM.

3. libgit2 thirdparty library is not compatible

There is an error at link this time: no such thing as strnlen in my standard libraries.
Fortunately, this is a known problem, already solved for mingw case by defining a p_strnlen.

Solution: I directly patched an include file to apply the known cure:
$ vim build/thirdParty/libgit2/libgit2-0.20.0/src/posix.h

where I replaced:
# define p_strnlen strnlen
with:
GIT_INLINE(size_t) p_strnlen(const char *s, size_t maxlen) {
        const char *end = memchr(s, 0, maxlen);
        return end ? (size_t)(end - s) : maxlen;
}

 

A Future version of libgit2 should fix it I hope, and the installation script should be changed to point to that version. Right now, I am again getting further away from automated route... Specially if build directory has to be erased at each code generation!

4. VM compiled by cmake fails to run a Pharo image

At least, it fails frankly, here is how:
$ ../results/Pharo.app/Contents/MacOS/Pharo generator.image
Bus error.

Err, so many hours going up in smoke, that can't be...

Solution: let's debug by generating a Xcode project from cmake and generating a VM from Xcode:
$ cd build
$ rm CMakeCache.txt 
$ cmake -G Xcode

Good news, that worked without error.


Hooray! But what's the difference ??? I can't tell, but at this stage, I don't care, I can test my changes with the right invocation:
$ ../results/Release/Pharo.app/Contents/MacOS/Pharo generator.image

5. Conclusion, it worked finally!

The object of this post is not to rant against Pharo team. On the contrary, hat down to Igor, Esteban, Camillo, and whoever participated, a huge work has been accomplished in the right direction, and it's me who departed a bit away from the official procedure.

But see how switching tools costs. Four evenings in my case including git, not accounting the time to blog it, let's hope that the investments will pay. This deserves some feedback. It's not that configure is better than cmake, nor svn than git, I think quite the contrary. I would expect more resilience though. The process is not polished enough.

I perfectly understand Pharo's choice. Squeak procedure was easier when not innovating too much, but adding cairo support is a sufficient reason for reconsidering what easy means!

It should be possible to automate all my actions. Once those low branded workarounds polished, they might be aggregated. Of course, this would be at the cost of maintaining a more and more complex CMakeVMMaker...

The opposite solution is to support only a restricted set of hard/soft platforms, and let every other one deal with its own problems, but how good is it in the long term? Everything rots, it's a source of future frustrations, and might restrain good wills.

For my tiny experimentations, I'm back to a an old school image based and hand crafted procedure. That's all right. But I must quickly write this post, at least it will help myself.

I'll retain the two very good points:
  • the compilation details are configured from within the image with CMakeVMMaker (at least, dirty hacks are easy)
  • a single repository with consistent platform specific C code and Smalltalk VMMaker code
 Now, let's concentrate on the vm content. We'll see later about the harder parts
  • how to manage the workflow with MC, filetree, github and my special brand
  • how to reconcile Eliot's work.