ros-infrastructure / bloom

A release automation tool which makes releasing catkin (http://ros.org/wiki/catkin) packages easier.
Other
58 stars 95 forks source link

Packaging ROS for homebrew #254

Open NikolausDemmel opened 10 years ago

NikolausDemmel commented 10 years ago

I'm opening this ticket to collect the thoughts and ideas that people have already had about this.

@wjwwood: Could you please copy your answer from [1] over here as a comment. I don't have enough karma to edit your post to get at the source.

I would say that generating formulae automatically with bloom is the only viable solution (as opposed to manually maintaining formulae). However, I actually started bottom up and experimented a little by manually creating some formulae (for now catkin, rospack, roslib) to see how the generated formulae should look like [2].

This has already revealed some issues. Most notably homebrew does at the moment not allow to specify a custom path where to install the formula contents. We would want to install e.g. in /usr/local/ros/hydro rather than /usr/local/ (or ideally /opt/ros/hydro, but homebrew has a strong promise to only touch /usr/local). Moreover, currently homebrew installs only stuff in the default FHS folders (bin, lib, etc, ...), but files/folders on the toplevel do not get installed (setup.bash, ...). Another issue is the question about python dependecies. On debian they all have apt-equivalents, but in brew world that is not done. However, there is the possibility for us to maintain such formulae for all the python module dependecies at least for the core packages. On the other hand, homebrew can at least depend on pip installed modules, such that their precense is checked. But it won't automatically call pip install for you. Maybe something in conjunction with rosdep can be done here. I would prefer sticking with pip for installing these.

Anyway, the install prefix issue has to be sorted out before automatic generation can be tackled. I asked the homebrew mailinglist, but didn't quite get enough info so far [3].

[1] http://answers.ros.org/question/130346/packaging-ros-with-homebrew/ [2] https://github.com/NikolausDemmel/homebrew-ros_experimental [3] http://librelist.com/browser//homebrew/2014/3/9/installing-group-of-formulae-into-a-different-folder/

mikepurvis commented 10 years ago

"Most notably homebrew does at the moment not allow to specify a custom path where to install the formula contents."

A homebrew installation can be placed wherever. What about simply having one homebrew installation per rosdistro? Then everything is FHS-relative within /opt/ros/hydro, eg for ROS Hydro homebrew. Could be a relatively simple script to untar the homebrew install, connect up the Hydro tap, and then symlink /usr/local/bin/ros-hydro-brew to /opt/ros/hydro/bin/brew.

ros-hydro-brew install navigation

The alternative would be engaging the Homebrew maintainers to push the necessary changes upstream.

wjwwood commented 10 years ago

@mikepurvis I tried to setup a ROS only homebrew in /opt/ros before and ran into some troubles. It seems some software expects to be placed into /usr/local. It's not impossible, but it has its own bumps in the road.

Also you could just install one version of ROS at a time to /usr/local with no install prefix.

wjwwood commented 10 years ago

Copied over from http://answers.ros.org/question/130346/packaging-ros-with-homebrew/:

I have looked to do it for a long time now, but I have never had the time to setup everything. For now, all the time I can afford to spend on it is to keep it building from source on OS X.

From my perspective, the main challenges to getting this working are as follows:

I think that generating Formulae for packages is the only sustainable way to approach this problem. There are nearly 800 packages in Hydro and 200+ in desktop-full. Packaging them by hand is error prone and unmaintainable. So coming up with a bloom generator for the formulae would be crucial. I started that work, but didn't get very far at all:

https://github.com/wjwwood/bloom_homebrew

The CI system for OS X has also been a challenge. I spent some time trying to setup a diskimage with COW for OS X which would behave like chroot/pbuilder for linux but ran into a lot of issues. The other thing I tried was using a VM and snapshots through a Python API, but that required Parallels which is not free and was slow. The Homebrew guys just clear /usr/local with git clean -fdx between builds for their "homebrew brewbot" system (https://www.kickstarter.com/projects/homebrew/brew-test-bot). I believe we (OSRF) are setting up a similar setup using jenkins on our build farms:

http://build.osrfoundation.org/view/SDFormat/job/sdformat-any-devel-homebrew-amd64/

If that is successful, I might try to use it for packaging ROS.

Finally, for bottle support (which would be awesome), we would need to also build each formula with the --build-bottle option, create the bottle, upload it somewhere (like download.ros.org/bottles) and then update the formula again, probably pushing the formula to https://github.com/ros/homebrew-hydro/.

So I have looked into it before, but as always progress on this is subject to my free time, which is in pretty low supply at the moment.

NikolausDemmel commented 10 years ago

@mikepurvis I think having additional brew installations is an interesting idea for a midterm makeshift solution, but not ideal. It would also mean that the user has to brew update/upgrade multiple times and complicates dependecy resolution (do we install all dependecies again for each distro (@wjwwood suggests that that might not even work without additional fixes in 3rd party software)? If not we don't have automatik resolution (rosdep could help) ).

Installing into /usr/local (only one distro at a time) also might be a good start, but we still have to find a solution for installing stuff that is not in FHS folders, like the setup.*sh files. Homebrew is quite hackable and the core devs very responsive, so I think we might be able to integrate a change that allows both custom install destination for ros formulae and linking of files ouside of FHS folders without too much effort. Let's see if this discussion [1] yields some more hints to a solution:

[1] http://librelist.com/browser//homebrew/2014/3/9/installing-group-of-formulae-into-a-different-folder/

mikepurvis commented 10 years ago

A possible advantage of a fully separate homebrew install is getting more control over the versions of dependencies, per REP3. The really big dependencies (eg, XQuartz, XCode tools) are installed outside of brew anyway, so it doesn't seem like it would be tons of duplication.

If I understand correctly, doing things this way would give the user choice to install in /opt/ros/hydro or /usr/local, at their option—whereas if the ros/hydro prefix is baked into the formulae, it's really just /usr/local/ros/hydro.

I'd be curious to know more about which formulae have path problems—among the ones we depend on, how many have issues? Is it possible we could push fixes to them, or are they intractable?

NikolausDemmel commented 10 years ago

I think versioned dependencies can be achieved also with one single homebrew installation, especially if we have a way to change the install path (which we could then also use for selected versioned dependecies, not only the ros formulae. The biggest potential conflict I see in REP3 is boost.). However, to fully support this will be challanging either way I assume. The main focus should be on support for the most recent distributions. It is an important point to keep in mind though.

The effective prefix would not necessarily need to be baked into the formula. I don't know how much is possible currently in homebrew, but if we need to extend it anyway, I imagine some form of grouping for custom install prefixes. So the formula says it wants custom install prefix "ros/hydro", which defaults to brew --prefix/ros/hydro, but can be globally configured by the user to any location he wants, including /opt/ros/hydro or /usr/local (I guess it is somewhat important to the homebrew people that per default, homebrew does not touch anything outside brew --prefix).

I would like to just be able to brew install ros-hydro-desktop and brew install ros-groovy-ros-base once ros/ros is tapped. Having to install a custom homebrew for each distribution with the implication of having to run update/upgrade n+1 number of times if I want to have n distributions installed makes me cringe a bit. It might be the more practical solution with less adjustments of homebrew (maybe not), but I'm not sure it's leading down the right path.

I'm not sure which formulae have path problems, but the wiki [1] indicates that it is probably not a matter of fixing just a bunch of formulae. It might be a different story if you only look at all dependecies of ROS (rather than all of homebrew), but no idea. Even if we don't do additional installations of homebrew, but rather custom install prefixes for just the ros formulae, this issue is still there if we want to provide some versioned dependecies. However, the versioned dependecies are even fewer than all the dependecies.

[1] https://github.com/Homebrew/homebrew/wiki/FAQ#wiki-why-does-homebrew-insist-i-install-to-usrlocal-with-such-vehemence

mikepurvis commented 10 years ago

Okay, sounds good. My apologies for derailing the discussion.

NikolausDemmel commented 10 years ago

No apologies needed. I value any alternatives and thoughts outside the box. Also, I'm no athority on which way this goes, just promoting my opinion ;-).

NikolausDemmel commented 10 years ago

@wjwwood : I have never used bloom myself. As I understand it there is quite some interplay between the bloom command line utility that a maintainer uses for releasing packages and that then triggering debians being built by some jenkins on a server. For the purpose of working on a formula generating backend, how much effort would there be to set up locally an environment where I can run my custom generator on a selected set of already released packages?

wjwwood commented 10 years ago

There are two ways to use bloom. First, you can simply use the generator to create the files needed for packaging a single catkin package on a specific platform. For example:

$ git clone https://github.com/ros/catkin.git
$ cd catkin
$ bloom-generate -h
usage: bloom-generate [-h] [rosrpm | rosdebian | rpm | debian] ...

Calls a generator on a local package, e.g. bloom-generate debian

optional arguments:
  -h, --help            show this help message and exit

generate commands:
  Call `bloom-generate [rosrpm | rosdebian | rpm | debian] -h` for help on a
  each generate command.

  [rosrpm | rosdebian | rpm | debian]

$ bloom-generate debian -h
usage: bloom-generate debian [-h]
                             [--place-template-files | --process-template-files]
                             [--os-name OS_NAME] [--os-version OS_VERSION]
                             [--ros-distro ROS_DISTRO] [-d] [--version]
                             [--unsafe]
                             [package_path]

Generates debian packaging files for a catkin package

positional arguments:
  package_path          path to or containing the package.xml of a package

optional arguments:
  -h, --help            show this help message and exit
  --place-template-files
                        places debian/* template files only
  --process-template-files
                        processes templates in debian/* only
  --os-name OS_NAME     OS name, e.g. ubuntu, debian
  --os-version OS_VERSION
                        OS version or codename, e.g. precise, wheezy
  --ros-distro ROS_DISTRO
                        ROS distro, e.g. groovy, hydro (used for rosdep)

global:
  -d, --debug           enable debug messages
  --version             prints the bloom version
  --unsafe              Makes bloom faster, but if there is an error then you
                        could run into trouble.

$ bloom-generate debian --os-name ubuntu --os-version precise --ros-distro hydro
==> Generating debs for ubuntu:precise for package(s) ['catkin']
No historical releaser history, using current maintainer name and email for each versioned changelog entry.
Package 'catkin' has dependencies:
Run Dependencies:
  rosdep key           => precise key
  cmake                => ['cmake']
  gtest                => ['libgtest-dev']
  python-argparse      => []
  python-catkin-pkg    => ['python-catkin-pkg']
  python-empy          => ['python-empy']
  python-nose          => ['python-nose']
Build and Build Tool Dependencies:
  rosdep key           => precise key
  gtest                => ['libgtest-dev']
  python-argparse      => []
  python-catkin-pkg    => ['python-catkin-pkg']
  python-empy          => ['python-empy']
  python-nose          => ['python-nose']
  cmake                => ['cmake']
==> Placing templates files in the 'debian' folder.
==> In place processing templates in 'debian' folder.
Expanding 'debian/changelog.em' -> 'debian/changelog'
Expanding 'debian/control.em' -> 'debian/control'
Expanding 'debian/rules.em' -> 'debian/rules'
$ ls debian/*
debian/changelog debian/compat    debian/control   debian/rules

debian/source:
format

I would start with a simple generator for Homebrew that can create a Homebrew formula for a single catkin package.

Once you have mastered that, you could move on the the next level of bloom which is to use a git repository to manage releases and generate one or more Homebrew formulae as part of the release process for ROS. For Debian, the released software goes through a few git branches, each giving the releaser an opportunity to patch the release, e.g. for ROS or for the OS or even for the particular OS version.

There is one level higher which is the release automation tool, which most people use. This tool automates the release process by cloning the release repo, doing a set of standard generations and branching commands, pushes the result and if the ROSDISTRO_INDEX_URL points to a repository that is on github.com it will open a pull request against it, to notify our build farm that new package (or new version of a package) has been released.

NikolausDemmel commented 10 years ago

@wjwwood : that was exactly what I was looking for. Thank you for the quick intro.

NikolausDemmel commented 10 years ago

I'm making progress on a basic generator. Quick question: Where is the logic that resolves dependecies like rospy to homebrew package ros/hydro/ros_comm?

Also: It's not quite clear to me how to get at the appropriate gbp repo / tar url from inside the bloom generator. I have to investigate what rosinstall generator does.

wjwwood commented 10 years ago

I'm making progress on a basic generator. Quick question: Where is the logic that resolves dependecies like rospy to homebrew package ros/hydro/ros_comm?

That is in rosdep: https://github.com/ros-infrastructure/rosdep/blob/master/src/rosdep2/gbpdistro_support.py#L108

Also: It's not quite clear to me how to get at the appropriate gbp repo / tar url from inside the bloom generator. I have to investigate what rosinstall generator does.

It uses the python-rosdistro library: https://github.com/ros-infrastructure/rosdistro

rosinstall_generator would be a good place to look.

NikolausDemmel commented 10 years ago

I'm making progress on a basic generator. Quick question: Where is the logic that resolves dependecies like rospy to homebrew package ros/hydro/ros_comm?

That is in rosdep: https://github.com/ros-infrastructure/rosdep/blob/master/src/rosdep2/gbpdistro_support.py#L108

Ah, it gets baked in the cache during update. That's why I didn't find it in the lookup code.

Also: It's not quite clear to me how to get at the appropriate gbp repo / tar url from inside the bloom generator. I have to investigate what rosinstall generator does.

It uses the python-rosdistro library: https://github.com/ros-infrastructure/rosdistro rosinstall_generator would be a good place to look.

Perfect. Actually, rosdistro might have enough info, such that the Formula can be generated for released packages without having a checkout with the package.xml. More on that later, once the basic proof of concept is working.

NikolausDemmel commented 10 years ago

What I don't understand yet why rosdep keys are mapped to a repo metapackage rather than to the individual ros package. Is the existance of this metapackage guaranteed and how is this enforced? Are those manually maintained? I'm talking only wet packages here.

wjwwood commented 10 years ago

I believe this is a bug in rosdep, the code I linked to above was written quite a long time ago and it looks like the debian code got updated, but not the homebrew code. Specifically I believe these lines:

https://github.com/ros-infrastructure/rosdep/blob/master/src/rosdep2/gbpdistro_support.py#L109-110

homebrew_name = '%s/%s/%s' % (get_owner_name(url),
                              release_name, rosdep_key)

Should instead be:

homebrew_name = '%s/%s/%s' % (get_owner_name(url),
                              release_name, pkg)

In order to match this: https://github.com/ros-infrastructure/rosdep/blob/master/src/rosdep2/gbpdistro_support.py#L116

NikolausDemmel commented 10 years ago

Ah yeah, thanks for pointing that out. I looked that function, but didn't notice it was out of sync with what happens for debians.

NikolausDemmel commented 10 years ago

First bunch of generated experimental formulae are up at https://github.com/NikolausDemmel/homebrew-ros_experimental .

A bunch of them install, but configuration / build runs into issues, I guess because no setup.bash is sourced. The issue is, that the setup files can currently not be linked into /usr/local by homebrew.

wjwwood commented 10 years ago

Nice work!

So, the issue with the setup files is something that we eventually have to deal with in order to be packaged by debian proper as well.

I can't remember if there is already an issue (opened or closed) for this or not. @dirk-thomas do you know of one?

dirk-thomas commented 10 years ago

ros/catkin#380

NikolausDemmel commented 10 years ago

Yeah I had to remove one file NO_DEFAULT_PATH manually somewhere in message_generation and then got up to roscpp. It's compilation failed because it didn't find the cmake-generated include devel/ros/common.h. It seems the only solution in the short-term is teaching homebrew how to install the setup files and sourcing them in the install like for the debs.

NikolausDemmel commented 10 years ago

I pushed an update. There are install instructions in the readme for creating symlinks for /usr/local/setup.bash etc manually with help of a small script. With this, I can install ros_base with homebrew. That's enough for a proof of concept.

Next steps are refactoring the generation code and deciding how this should work for the automatic generation via jenkins. Moreover, we need to engage with homebrew and make the necessary changes happen.

I won't be able to work on this much for the next couple of weeks. We'll see then.

NikolausDemmel commented 10 years ago

To summarize the things that need to happen from my point of view (some more or less important):

critical:

nice to have:

References:

wjwwood commented 10 years ago

Sounds good, thanks for looking into it.

I don't have much time to allocate to this, but if something I can help with is really blocking you just let me know.

mikepurvis commented 9 years ago

I may be interested in working on this a bit over the holidays. Is there a particular contained piece of the task which would be reasonable to tackle in the next few weeks?

wjwwood commented 9 years ago

Short of re-summarizing this issue I think that most if not all of the points in @NikolausDemmel's comment are still blocking:

https://github.com/ros-infrastructure/bloom/issues/254#issuecomment-37401069

I'm not sure if any of them lend themselves to easily being tackled in the short term. I guess the first thing to do would be to make it so that generating Homebrew formulae from catkin packages is straight forward, you could start with @NikolausDemmel's prototype, which is linked above.

NikolausDemmel commented 9 years ago

I agree with @wjwwood, I don't think the situation has changed much since I had a look at it in March. Urfortunately, I currently have zero spare cycles having recently started in a new job. My list of issues/todos above is of course not definitive, but just what I had in mind when I was looking at it.

mikepurvis commented 9 years ago

I can look at building an automated generator, but it seems like the dependency resolution issue in Homebrew is a real deal-breaker in terms of having something usable.

For a generator implementation, I guess the ideal would be a two-parter— something which integrates with bloom, in order to support OSX-specific patching as necessary, and then also a piece which synchronizes all of the "built" formulae and places them in a Homebrew-consumable repository. Is that a reasonable statement of what's to be done?

NikolausDemmel commented 9 years ago

@wjwwood is probably in a better position to answer your question. Just one comment: It might be worthwhile looking at how RPM support was added to bloom in https://github.com/ros-infrastructure/bloom/pull/228 and related pull requests.

mikepurvis commented 9 years ago

My understanding is that adding RPM support was relatively straightforward due to the existence of git-buildpackage-rpm. When contemplating supporting systems like homebrew, cygwin, etc, we're facing having to produce build specs from bloom which will be executed from a non-Jenkins, non-Linux environment.

The first version of that environment for Homebrew is probably a single-step system which crawls the rosdistro, and assembles/updates the requisite homebrew tap, to facilitate a user locally building. This could be as simple as a Travis job which just has a secret key to push changes to the tap.

A later version of the system could see it attached to OSX-based CI machinery to create and host bottles. Travis actually has provisional OS X support, interestingly, so it's not out of the question to imagine that a single Travis job could just grind through building and uploading every bottle as it went.


Ugh, I just thought about it some more, and building bottling infrastructure is a big mess. A more realistic path on that score might be for interested parties in the ROS community to sponsor the brew test-bot crew to bottle ROS for us.

Of course, all of this is dependent first on getting to a reasonable source-install story.

cottsay commented 9 years ago

@mikepurvis - Just so you know, I created the RPM generator in Bloom without any knowledge of git-buildpackage-rpm. The branches that it generates are mock scm compatible, and can be built using the Fedora buildsystem, koji. I adapted the existing Jenkins code to create the buildfarm we use to build the RPM packages.

mikepurvis commented 9 years ago

Ah, my apologies, @cottsay.

NikolausDemmel commented 6 years ago

After having to reinstall on my mac from source once again, I had a very brief look at the state of this. The generator proof of concept is out of date with its dependencies (e.g. rosdep) and doesn't work as is. The easiest would maybe be to start again with the current debian / rpm generators and adapt them into homebrew geneators with the ideas from the proof of concept code.

Concerning the critical issues mentioned above (https://github.com/ros-infrastructure/bloom/issues/254#issuecomment-37401069), I had some thoughts for workarounds. I'll just dump it here in case some day someone picks this up:

Homebrew doesn't allow to link kegs into a custom prefix like /opt/ros/lunar/, i.e. only the default prefix (/usr/local), which means the linking the formulae for the same pacakge in multiple ROS distros would conflict. Moreover, homebrew doesn't allow to link files in the top-level (directly into /usr/local/), but instead only in the standard FHS subfolders like lib,bin,share, etc. This means that we cannot link into /usr/local/ and have homebrew create the setup.sh files when linking the catkin keg.

The linking code in homebrew is part of the keg class, and thus it seems it cannot be changed by writing some custom code in your formulae. In homebrew we will likely meet some resistance to add functionality to work around this limitation upstream.

A workaround could be to link everything into some custom folder, inside one of the allowed subfolders, maybe /usr/local/share/opt/ros/lunar. This would allow to have multiple distros installed, and requires no changes to homebrew. It would be great if that would then be usable from a standard path by having a symlink /opt/ros/ --> /usr/local/share/opt/ros/.

Additionally, formulae have a post-install script, which is called when a formula is installed (both from source and also from a bottle). We could have some custom code e.g. to setup the /opt/ros/ --> /usr/local/share/opt/ros/ link if it doesn't exist.

Alternatively, we could make all formulae keg-only and add an external command to brew (can be setup by just added our Tap) which does the linking of the kegs directly into /opt/ros/..., e.g. with brew ros link ..... We could even (ab-)use the post-install script to call this automatically. The disadvantage is that it is more complicated to implement with all corner cases, and also, it isn't such a great user-experience, since our formulae would behave even more differently from regular homebrew formulae.

One could also use brew ros not just for linking the kegs into /opt/ros, but as a general interface for installing packages, i.e. make brew ros install ... install the formula and do the linking. While this again makes things even more complex to implement, it gives even better control and would even allow to do things like installing pip dependencies (maybe using rosdep), which homebrew itself doesn't support. However, it might be strange if brew install foo-pkg is possible, but doesn't do the right thing.

The cost of maintaining this with ever-changing homebrew might be quite high, so I would favour a solution with little as possible dependence on homebrew internals.

As for the dependency resolution speed issue, I think it is still too slow. However, in the linked upstream issue, one of the homebrew contributors mentioned he had prototyped and tested a possible solution, but it seems like that never got anywhere (also because noone was pushing for it). If we raise the need again, we might get some help to get this fixed in homebrew.

mikepurvis commented 6 years ago

@NikolausDemmel Thanks for your ongoing attention to this, and willingness to dig into the gritty details. I think from my point of view (as the owner and a user of ros-install-osx over the past 2ish years), it's been tremendously frustrating just the scope of things which break when trying to run ROS under OSX via Homebrew. Iteration on ros-install-osx is driven mostly by my own personal need, but it basically boils down to:

  1. My Mac's ROS installation is getting stale.
  2. Cruise through the open issues on ros-install-osx to get a sense of what I'm up against.
  3. Spend a day or two grinding through whatever it is.
  4. Prove to myself that it works from scratch by nuking my own homebrew installation and installed pip packages and redoing everything from scratch.
  5. Within a month or two something is broken again, whether QT, python/pip, SIP, Gazebo, boost, opencv, MacOS "frameworks", homebrew dependency madness, or other stuff.

By far the biggest help in all this would be having a robust CI setup which lets me test across multiple versions of macOS and also check both XCode and Command Line Tools. I was hopeful that Travis would fit the bill for this, but it's too rigid in the image that it supports, and the VM is too starved for resources to be able to complete within the 1hr time limit.

Anyway. All that to say: the idea of being able to just brew install ROS packages and get pre-built bottles is super exciting and appealing, but given the number of hacks it would take to get there and the number of things which seem to break even using a bog standard build-from-source workflow, I'm just not sure the effort will pay off.

It feels to me like the best story for users in the short term is a) streamlining the build-from-source story as much as possible and establishing the infrastructure necessary to ensure that once it's working it stays working, with a possible intent to work toward b) stashing the pre-build installspace somewhere as a tarball, with some metadata about the exact versions of homebrew bottles which need to be present for it to work at runtime, so that an "install ROS on OS X" tool could simply unpack the tarball and ensure that the required bottles are present.

wjwwood commented 6 years ago

I agree with the sentiment that macOS progress is gated on the ability to regularly build packages and to have enough manpower to overcome the volatility in Homebrew. Until you can have a job that just builds all of desktop-full (for example) regularly in a CI fashion and enough people to keep it up to date, then resolving packaging issues doesn't make a lot of sense to me.

It's worth pointing out though, that having the ability to bottle what works might make travis more viable because you don't have to build everything in one job, instead you can pour bottles for dependencies and build only what's new. Even then there will be packages that don't work, but at least it might push the problem down the line a bit.

My efforts on packaging (back when I had time to spend on this topic) were forestalled by not having reproducible builds as well. And things like brew bot and travis CI didn't exist.