Part referencing other part?

At first blush it sounds like you have two parts, one that depends on the other. If A depends on B, then if you add after to A, B will be staged before A is built:

parts:
  A:
    plugin: cmake
    # ...
    after: [B]

Then you can use $SNAPCRAFT_STAGE in the YAML as the variable pointing into the staging area. However, there’s something that’s bothering me about this:

What does this mean, exactly? Specifically, what do you mean by “permanent”? This causes my concern because once the snap is built, it’ll be mounted to another location.

Yeah, the after part to control order of operations is already in place.

To answer the other question, my language my have been imprecise or weird. It’s moot now. I can give you details when I email you some other notes, but TLDR: hacky crap that predates my involvement with this.

My remaining related question, though, is this: dependency 2, once built, provides a C library that ROS needs to build. The ROS side looks like this.

FIND_LIBRARY(SIMPLECAT_LIBRARY simplecat /home/myuser/simplecat/lib/)

What is the Snapcraft best practice? This makes it look like I can maybe copy them into $SNAP/usr/lib/ manually and then modify our ROS install to find them there, but all instructions I can find apply to runtime, not buildtime.

Are you in control of dependency 2? Best practice (even ignoring snaps) would be for it to create a CMake config file or finder and install it into the right place. Something along the lines of this. Then it can be found in the staging area by simply telling CMake to look there for the config file.

Also:

Man, been there :smiley: .

RE: control, yes and no. “Yes” in that we’re working from a fork of someone else’s repo. (Robotics engineer modified some files to do… something?) “No” in that none of us wrote the build process and I’m not entirely confident I can implement this without filling a lot of gaps in my understanding of CMake. (I wish this was all written in… practically any other language.) I do control where this dependency builds, so I can build straight to a shared location, I just need to know the Snapcraft best practice for creating and using shared paths between parts during buildtime so ROS can find it.

This make sense?

Makes total sense. Okay, let’s assume we can’t change dependency 2. If it’s its own part, and you run snapcraft stage, where does the simplecat library end up installed? stage/lib/? If so, honestly, this should just work because the Catkin plugin should set CMAKE_LIBRARY_PATH appropriately. Unfortunately, it does not, so I’ll log a bug for that. Until then, try this (which will also make sure my bug fix will work):

parts:
    # ... 
    ros-part:
        plugin: catkin
        # ...
        catkin-cmake-args: -DCMAKE_LIBRARY_PATH=$SNAPCRAFT_STAGE/lib
        after: [dependency-2] # you get the idea

As far as I can tell, Simplecat doesn’t end up there. The only files I see in stage/lib/ belong to the Ethercat master, which is essentially the device driver for which Simplecat provides an interface. You can see the one we forked here. We skip building its examples, so our manual build is just…

# clone it, cd into it
mkdir build
cd build
cmake ../
make

We have one ROS package that relies on it. In that CMakeLists.txt, we have the FIND_LIBRARY command from my earlier post:

FIND_LIBRARY(SIMPLECAT_LIBRARY simplecat /home/myuser/simplecat/lib/)

…and…

include_directories(/home/myuser/simplecat/this-other-path-with-custom-crap)

Time constraints being what they are, I’m leaning towards to just kicking this whole thing way down the field, building an image that already has all this stuff installed manually, and continuing to use Ubuntu Server with our shiny new ROS snap for the time being. That at least works. I’m concerned that even if we can get this working, the Ethercat libraries and modules will present a much more significant issue.

Oh interesting, it seems that it’s missing install rules. Okay, in that case, you’ll need to install it by hand by using the install keyword, something like:

parts:
  # ...
  simplecat:
    plugin: cmake
    source: blahblah
    install: |
      # You're in the build dir now. We're essentially
      # writing install rules in shell.
      mkdir -p $SNAPCRAFT_PART_INSTALL/lib
      cp lib/* $SNAPCRAFT_PART_INSTALL/lib/
      # Assuming headers are required:
      mkdir -p $SNAPCRAFT_PART_INSTALL/include
      cp include/* $SNAPCRAFT_PART_INSTALL/include/

Assuming headers are required, you’ll also need to add something like -DCMAKE_INCLUDE_PATH=$SNAPCRAFT_STAGE/include to the catkin-cmake-args in the example a few posts above.

If you need to kick the can down the road no problem, but I’m here if you want to continue.

Great, I’ll give that a shot. Thank you! This isn’t a catkin package, I’m using the nil plugin and setting source to my repo. Been building manually so far. Is that the right approach?

$SNAPCRAFT_PART_INSTALL/lib is specific to this part, right? If so, I’m still missing the correct way to reference it from my ros part, which is what you so generously helped with previously.

Since the part in question uses CMake, I suggest trying the cmake plugin. Less you need to do manually. I actually suggest trying it without any hackery first-- maybe it has install rules I missed! So try this, and see how it works:

parts:
  # ...
  simplecat:
    plugin: cmake
    source: blahblah

If it errors out saying something that effectively means “what the heck, no install rules” try this (assuming the lib gets built in <build dir>/lib and the includes get placed into <build dir>/include):

parts:
  # ...
  simplecat:
    plugin: cmake
    source: blahblah
    artifacts: ['lib', 'include']

If that still doesn’t work, you can try the more manual shell hackery outlined above, or use the nil plugin as you’re doing and do EVERYTHING by hand. Yucky, but doable.

Bingo. Every part goes through a lifecycle of steps:

  • pull: Pull the source code for this part
  • build: Build/install this part (place things into $SNAPCRAFT_PART_INSTALL)
  • stage: Place the installed stuff into the common staging area (i.e. migrate everything in $SNAPCRAFT_PART_INSTALL into staging area)
  • prime: Place the final stuff into the priming area (which after all parts are complete, ends up being the snap)

So if you need to build part A that depends on part B, you need to make sure part B is already in the staging area before part A is built. That’s exactly what the after keyword does. By saying after: [B] you’re saying “please stage part B before building this part” which means you can use the $SNAPCRAFT_STAGE variable to access its libs, includes, etc. That’s what this post is covering.

Ah, this is great. I think I’ve been misunderstanding the lifecycle a bit and didn’t realize there even was a shared staging area! facepalm

You anticipated the “what the heck, no install rules” correctly. Unfortunately, it looks like nothing winds up in build other than CMakeCache.txt, cmake_install.cmake, and the CMakeFiles directory. When building manually, I always have to run the cmake command from within my manually-created build dir.

mkdir build
cd build
cmake ../
make

You can see this is the process demonstrated in the project’s build_all.sh script, here. We don’t need the example files, so we just run those first commands.

This feels like a new iteration of hacky crap but this populates the parts/simplecat/build/ directory:

simplecat:
  plugin: nil
  source: blah
  build-packages:
    - cmake
  prepare: |
    cd ../
    cd build
    cmake ../src
    make

Huh, the cmake plugin does an out-of-source build as well, not sure why that isn’t working. Regardless, yeah, if that works, might as well also add the “install rules” (I also suggest changing the “prepare” to “build”):

simplecat:
  plugin: nil
  source: blah
  build-packages:
    - cmake
  build: |
    cd ../
    cd build
    cmake ../src
    make
  install: |
    # You're in the build dir now. We're essentially
    # writing install rules in shell.
    mkdir -p $SNAPCRAFT_PART_INSTALL/lib
    cp lib/* $SNAPCRAFT_PART_INSTALL/lib/
    # Assuming headers are required:
    mkdir -p $SNAPCRAFT_PART_INSTALL/include
    cp include/* $SNAPCRAFT_PART_INSTALL/include/

We’re in business! The installed files are copied into stage. I imagine I can then feed the simplecat lib path to catkin via catkin-cmake-args so it builds correctly? Going to poke at it some more now.

Yes exactly! The lib ended up in $SNAPCRAFT_STAGE/lib, then? And the includes?

Just lib, there’s no includes. I also modified it to copy the src dir and its subdirectories, since there’s something in there that the ROS install wants.

Just an update before I check out for the long weekend, I made it to a place where everything seems to build correctly, but I’m getting errors that suggest I need to mess with the install rules a bit more. I’m certainly equipped to do that at this point. Good things all around. Thanks for your help, once again!

Awesome! You know where to find us if you have more questions :slight_smile: .

Hi, has anybody tried this lately?

Some of the packages in my ROS workspace need a library that is provided through a previously staged part, more or less as follows:

parts:
  library:
    plugin: dump
    source: /foo/bar/library
    override-build: |
      snapcraftctl build
      ./install_snap.sh # This basically creates some symbolic links and copy the files where needed
    stage:
      - usr/*
      - etc/*
  ros-workspace:
    plugin: catkin
    rosdistro: indigo
    source-space: /foo/bar/catkin_ws/src 
    catkin-packages:
      - package A
      - package B # Some of these packages have to be linked against the previously staged library
      - ...
      - package N
    catkin-cmake-args:
      - -DCMAKE_LIBRARY_PATH=$SNAPCRAFT_STAGE/usr/local/lib
    after: [library]

According to this post it should work, however, the linking fails since the DCMAKE_LIBRARY_PATH seems to be ignored. I can check that the parameter is properly taken by the plugin but not sure if it is doing something with it. I also can check how the library is properly staged by the part.

Is it necessary to adapt the /etc/ld.so.conf at the host or staged to make it visible for ldd?

For now, in order to solve that, I had to modify the CMakeList.txt and include the absolute path to the libraries part when linking with something like $SNAPCRAFT_STAGE/use/local/lib/libfoo.so.