Repository files navigation

ros-template

This repository is a template for a containerized ROS 2 project. It uses make to implement the following utility commands:

CommandDescription
make buildBuilds the colcon workspace.
make testTests the colcon workspace.
make devEnters a bash shell inside the container containing the project's dependencies and build environment.
make finalBuilds the colcon workspace and copies the resulting artifacts into a minimal ros-core image tagged {name}:{distro}-final.
make refreshCauses the previous set of commands to rebuild the image when run.
make cleanUntags and removes any installed images.
make sync-submodulesManually syncs and recursively updates git submodules.

Why?

Working in ROS is often unnecessarily challenging due to the ecosystem relying on rosdep to map the dependency keys listed in projects' package.xml to those in linux distributions' repositories. This causes ROS distributions to be pinned to specific releases of specific linux distributions. Because individual ROS installations are often very complex and coupled to specific versions of ubuntu, it is often desirable to run the entire environment inside a docker container.

However, developing inside a containerized environment presents a very large pit of potential mistakes. Features like proper caching, x11 forwarding, a properly permissioned workspace volume, and even smaller things like an ergonomic development environment are all easy to mess up and cumbersome to implement.

Structure

This template project acts as the ros/colcon workspace, with all packages to be built going in src.

Containerfile contains build instructions for several stages of a container build, which can be used independently. Makefile contains the afformentioned scripts for using the container image. The images produced by the makefile's commands are:

  • {name}:{distro}-base (for make build), extends ros-{distro}-desktop-full
  • {name}:{distro}-dev (for make dev), extends base
  • {name}:{distro}-final (for make final), extends {distro}-ros-core

Where {name} is to be replaced with your project's name.

Make commands that rely on a specific image existing (make build, make test, make dev) will build the image first if it does not already exist. Afterwards, they will only rebuild the image if changes have been made to the Containerfile, or if they are forced to rebuild (make refresh, make clean). Since the images share most of the same layers, only your first command should take long, and the rest should be relatively quick. make build will also mount your entire project into /workspace, meaning build artifacts (build, install, log) are cached by default.

make dev will, by default, bridge the following between the host and container:

  • /workspace volume with rw access
  • host network
  • host user namespace
  • host pid namespace
  • host ipc namespace
  • X11

All the commands and syntax used is intended to be compatible with podman, docker, or any other OCI complaint container engine.

Usage

To set up the template, replace ROS_DISTRO and REPO_NAME in Makefile with your desired distro and project name. If you're using devcontainers, replace ROS_DISTRO in .devcontainer.json as well.

Installing Packages

This template, by default, does not support ros-specific tooling for dependency or repository management. This is for several reasons, but the most important one is that we can only guarantee ros-specific tooling exists inside the container's ros environment. This means tools such as rosdep and vcstool must be ran on the workspace inside the container, necessitating the workspace be copied into the container as part of the base image. This would destroy our caching because any changes to the workspace would cause lengthy rebuilds.

The consequence of not supporting tools like rosdep inside the container is that, to install packages, you must manually find their apt keys and add them to the apt-get install block inside the base layer of the containerfile. Additionally, if you want to use the make final feature, you must add runtime dependencies to the apt-get install block inside the final layer.

To add source dependencies, use git submodules inside src. For example:

git submodule add https://github.com/ros2/examples.git src/examples

You can edit submodules in .gitmodules (full spec here), and manually sync them with make sync-submodules. By default, all the build commands will ensure the workspace's submodules are synced to .gitmodules and updated. If you want to pin submodules to specific versions, you should run git checkout <commit> from their directories.

Questions and Limitations

You only explain how to install packages from apt. What if I need dependencies that only exist in pypi?

To add additonal dependencies via pip, for example, make a new RUN command below that looks something like:

RUN pip install antigravity

Great, but what if I have multiple packages with conflicting versions of dependencies?

A constraint of this setup is that we are completely tied to one specific version of every ros and ubuntu package. Anything that doesn't fit those versions needs to be ported or can't be used.

If the messiness of dependency management bothers you as much as it bothered me, you may want to check out the real solution to these problems, nix. I will refrain from shilling too hard, but there exists nix-ros-overlay to enable working with ros packages from nix. There is also robostack, which is a possibly more mature solution that seeks to address similar problems for ros specifically.

Back to the question: Use conda inside a container at your own risk... I heard the last person who did it was found years later in a shack in the woods.

I don't use VSCode with devcontainers. How can I edit files with language server support from within the container with <insert my editor here>?

Ha

This specific issue is very annoying. So annoying that I wrote a blog post about it. In short, a fundamental flaw of container based development is that you, and any other contributors, will need to add their personal tooling to the dev stage in the containerfile, and not commit it or hope it doesn't conflict with other contributors'.

If this again bothers you, both nix and pixi (the tool that robostack is based on) fix this problem by not installing your dependencies inside a full isolated system from the host.

It sounds like you really dislike containers and container based development. Why did you make this?

This template only depends on a container engine, git, and make. These, unlike nix or pixi/conda, are tools almost everyone, regardless of their os, has installed on their system. Perhaps one day things may be different, but until then, I am but a servant to network effects.

About

a sane containerized ros setup using make

Resources

Stars

7 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

Repository files navigation

ros-template

This repository is a template for a containerized ROS 2 project. It uses make to implement the following utility commands:

CommandDescription
make buildBuilds the colcon workspace.
make testTests the colcon workspace.
make devEnters a bash shell inside the container containing the project's dependencies and build environment.
make finalBuilds the colcon workspace and copies the resulting artifacts into a minimal ros-core image tagged {name}:{distro}-final.
make refreshCauses the previous set of commands to rebuild the image when run.
make cleanUntags and removes any installed images.
make sync-submodulesManually syncs and recursively updates git submodules.

Why?

Working in ROS is often unnecessarily challenging due to the ecosystem relying on rosdep to map the dependency keys listed in projects' package.xml to those in linux distributions' repositories. This causes ROS distributions to be pinned to specific releases of specific linux distributions. Because individual ROS installations are often very complex and coupled to specific versions of ubuntu, it is often desirable to run the entire environment inside a docker container.

However, developing inside a containerized environment presents a very large pit of potential mistakes. Features like proper caching, x11 forwarding, a properly permissioned workspace volume, and even smaller things like an ergonomic development environment are all easy to mess up and cumbersome to implement.

Structure

This template project acts as the ros/colcon workspace, with all packages to be built going in src.

Containerfile contains build instructions for several stages of a container build, which can be used independently. Makefile contains the afformentioned scripts for using the container image. The images produced by the makefile's commands are:

  • {name}:{distro}-base (for make build), extends ros-{distro}-desktop-full
  • {name}:{distro}-dev (for make dev), extends base
  • {name}:{distro}-final (for make final), extends {distro}-ros-core

Where {name} is to be replaced with your project's name.

Make commands that rely on a specific image existing (make build, make test, make dev) will build the image first if it does not already exist. Afterwards, they will only rebuild the image if changes have been made to the Containerfile, or if they are forced to rebuild (make refresh, make clean). Since the images share most of the same layers, only your first command should take long, and the rest should be relatively quick. make build will also mount your entire project into /workspace, meaning build artifacts (build, install, log) are cached by default.

make dev will, by default, bridge the following between the host and container:

  • /workspace volume with rw access
  • host network
  • host user namespace
  • host pid namespace
  • host ipc namespace
  • X11

All the commands and syntax used is intended to be compatible with podman, docker, or any other OCI complaint container engine.

Usage

To set up the template, replace ROS_DISTRO and REPO_NAME in Makefile with your desired distro and project name. If you're using devcontainers, replace ROS_DISTRO in .devcontainer.json as well.

Installing Packages

This template, by default, does not support ros-specific tooling for dependency or repository management. This is for several reasons, but the most important one is that we can only guarantee ros-specific tooling exists inside the container's ros environment. This means tools such as rosdep and vcstool must be ran on the workspace inside the container, necessitating the workspace be copied into the container as part of the base image. This would destroy our caching because any changes to the workspace would cause lengthy rebuilds.

The consequence of not supporting tools like rosdep inside the container is that, to install packages, you must manually find their apt keys and add them to the apt-get install block inside the base layer of the containerfile. Additionally, if you want to use the make final feature, you must add runtime dependencies to the apt-get install block inside the final layer.

To add source dependencies, use git submodules inside src. For example:

git submodule add https://github.com/ros2/examples.git src/examples

You can edit submodules in .gitmodules (full spec here), and manually sync them with make sync-submodules. By default, all the build commands will ensure the workspace's submodules are synced to .gitmodules and updated. If you want to pin submodules to specific versions, you should run git checkout <commit> from their directories.

Questions and Limitations

You only explain how to install packages from apt. What if I need dependencies that only exist in pypi?

To add additonal dependencies via pip, for example, make a new RUN command below that looks something like:

RUN pip install antigravity

Great, but what if I have multiple packages with conflicting versions of dependencies?

A constraint of this setup is that we are completely tied to one specific version of every ros and ubuntu package. Anything that doesn't fit those versions needs to be ported or can't be used.

If the messiness of dependency management bothers you as much as it bothered me, you may want to check out the real solution to these problems, nix. I will refrain from shilling too hard, but there exists nix-ros-overlay to enable working with ros packages from nix. There is also robostack, which is a possibly more mature solution that seeks to address similar problems for ros specifically.

Back to the question: Use conda inside a container at your own risk... I heard the last person who did it was found years later in a shack in the woods.

I don't use VSCode with devcontainers. How can I edit files with language server support from within the container with <insert my editor here>?

Ha

This specific issue is very annoying. So annoying that I wrote a blog post about it. In short, a fundamental flaw of container based development is that you, and any other contributors, will need to add their personal tooling to the dev stage in the containerfile, and not commit it or hope it doesn't conflict with other contributors'.

If this again bothers you, both nix and pixi (the tool that robostack is based on) fix this problem by not installing your dependencies inside a full isolated system from the host.

It sounds like you really dislike containers and container based development. Why did you make this?

This template only depends on a container engine, git, and make. These, unlike nix or pixi/conda, are tools almost everyone, regardless of their os, has installed on their system. Perhaps one day things may be different, but until then, I am but a servant to network effects.

About

a sane containerized ros setup using make

Resources

Stars

7 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

ros-template

This repository is a template for a containerized ROS 2 project. It uses make to implement the following utility commands:

CommandDescription
make buildBuilds the colcon workspace.
make testTests the colcon workspace.
make devEnters a bash shell inside the container containing the project's dependencies and build environment.
make finalBuilds the colcon workspace and copies the resulting artifacts into a minimal ros-core image tagged {name}:{distro}-final.
make refreshCauses the previous set of commands to rebuild the image when run.
make cleanUntags and removes any installed images.
make sync-submodulesManually syncs and recursively updates git submodules.

Why?

Working in ROS is often unnecessarily challenging due to the ecosystem relying on rosdep to map the dependency keys listed in projects' package.xml to those in linux distributions' repositories. This causes ROS distributions to be pinned to specific releases of specific linux distributions. Because individual ROS installations are often very complex and coupled to specific versions of ubuntu, it is often desirable to run the entire environment inside a docker container.

However, developing inside a containerized environment presents a very large pit of potential mistakes. Features like proper caching, x11 forwarding, a properly permissioned workspace volume, and even smaller things like an ergonomic development environment are all easy to mess up and cumbersome to implement.

Structure

This template project acts as the ros/colcon workspace, with all packages to be built going in src.

Containerfile contains build instructions for several stages of a container build, which can be used independently. Makefile contains the afformentioned scripts for using the container image. The images produced by the makefile's commands are:

  • {name}:{distro}-base (for make build), extends ros-{distro}-desktop-full
  • {name}:{distro}-dev (for make dev), extends base
  • {name}:{distro}-final (for make final), extends {distro}-ros-core

Where {name} is to be replaced with your project's name.

Make commands that rely on a specific image existing (make build, make test, make dev) will build the image first if it does not already exist. Afterwards, they will only rebuild the image if changes have been made to the Containerfile, or if they are forced to rebuild (make refresh, make clean). Since the images share most of the same layers, only your first command should take long, and the rest should be relatively quick. make build will also mount your entire project into /workspace, meaning build artifacts (build, install, log) are cached by default.

make dev will, by default, bridge the following between the host and container:

  • /workspace volume with rw access
  • host network
  • host user namespace
  • host pid namespace
  • host ipc namespace
  • X11

All the commands and syntax used is intended to be compatible with podman, docker, or any other OCI complaint container engine.

Usage

To set up the template, replace ROS_DISTRO and REPO_NAME in Makefile with your desired distro and project name. If you're using devcontainers, replace ROS_DISTRO in .devcontainer.json as well.

Installing Packages

This template, by default, does not support ros-specific tooling for dependency or repository management. This is for several reasons, but the most important one is that we can only guarantee ros-specific tooling exists inside the container's ros environment. This means tools such as rosdep and vcstool must be ran on the workspace inside the container, necessitating the workspace be copied into the container as part of the base image. This would destroy our caching because any changes to the workspace would cause lengthy rebuilds.

The consequence of not supporting tools like rosdep inside the container is that, to install packages, you must manually find their apt keys and add them to the apt-get install block inside the base layer of the containerfile. Additionally, if you want to use the make final feature, you must add runtime dependencies to the apt-get install block inside the final layer.

To add source dependencies, use git submodules inside src. For example:

git submodule add https://github.com/ros2/examples.git src/examples

You can edit submodules in .gitmodules (full spec here), and manually sync them with make sync-submodules. By default, all the build commands will ensure the workspace's submodules are synced to .gitmodules and updated. If you want to pin submodules to specific versions, you should run git checkout <commit> from their directories.

Questions and Limitations

You only explain how to install packages from apt. What if I need dependencies that only exist in pypi?

To add additonal dependencies via pip, for example, make a new RUN command below that looks something like:

RUN pip install antigravity

Great, but what if I have multiple packages with conflicting versions of dependencies?

A constraint of this setup is that we are completely tied to one specific version of every ros and ubuntu package. Anything that doesn't fit those versions needs to be ported or can't be used.

If the messiness of dependency management bothers you as much as it bothered me, you may want to check out the real solution to these problems, nix. I will refrain from shilling too hard, but there exists nix-ros-overlay to enable working with ros packages from nix. There is also robostack, which is a possibly more mature solution that seeks to address similar problems for ros specifically.

Back to the question: Use conda inside a container at your own risk... I heard the last person who did it was found years later in a shack in the woods.

I don't use VSCode with devcontainers. How can I edit files with language server support from within the container with <insert my editor here>?

Ha

This specific issue is very annoying. So annoying that I wrote a blog post about it. In short, a fundamental flaw of container based development is that you, and any other contributors, will need to add their personal tooling to the dev stage in the containerfile, and not commit it or hope it doesn't conflict with other contributors'.

If this again bothers you, both nix and pixi (the tool that robostack is based on) fix this problem by not installing your dependencies inside a full isolated system from the host.

It sounds like you really dislike containers and container based development. Why did you make this?

This template only depends on a container engine, git, and make. These, unlike nix or pixi/conda, are tools almost everyone, regardless of their os, has installed on their system. Perhaps one day things may be different, but until then, I am but a servant to network effects.

About

a sane containerized ros setup using make

Resources

Stars

7 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

ros-template

This repository is a template for a containerized ROS 2 project. It uses make to implement the following utility commands:

CommandDescription
make buildBuilds the colcon workspace.
make testTests the colcon workspace.
make devEnters a bash shell inside the container containing the project's dependencies and build environment.
make finalBuilds the colcon workspace and copies the resulting artifacts into a minimal ros-core image tagged {name}:{distro}-final.
make refreshCauses the previous set of commands to rebuild the image when run.
make cleanUntags and removes any installed images.
make sync-submodulesManually syncs and recursively updates git submodules.

Why?

Working in ROS is often unnecessarily challenging due to the ecosystem relying on rosdep to map the dependency keys listed in projects' package.xml to those in linux distributions' repositories. This causes ROS distributions to be pinned to specific releases of specific linux distributions. Because individual ROS installations are often very complex and coupled to specific versions of ubuntu, it is often desirable to run the entire environment inside a docker container.

However, developing inside a containerized environment presents a very large pit of potential mistakes. Features like proper caching, x11 forwarding, a properly permissioned workspace volume, and even smaller things like an ergonomic development environment are all easy to mess up and cumbersome to implement.

Structure

This template project acts as the ros/colcon workspace, with all packages to be built going in src.

Containerfile contains build instructions for several stages of a container build, which can be used independently. Makefile contains the afformentioned scripts for using the container image. The images produced by the makefile's commands are:

  • {name}:{distro}-base (for make build), extends ros-{distro}-desktop-full
  • {name}:{distro}-dev (for make dev), extends base
  • {name}:{distro}-final (for make final), extends {distro}-ros-core

Where {name} is to be replaced with your project's name.

Make commands that rely on a specific image existing (make build, make test, make dev) will build the image first if it does not already exist. Afterwards, they will only rebuild the image if changes have been made to the Containerfile, or if they are forced to rebuild (make refresh, make clean). Since the images share most of the same layers, only your first command should take long, and the rest should be relatively quick. make build will also mount your entire project into /workspace, meaning build artifacts (build, install, log) are cached by default.

make dev will, by default, bridge the following between the host and container:

  • /workspace volume with rw access
  • host network
  • host user namespace
  • host pid namespace
  • host ipc namespace
  • X11

All the commands and syntax used is intended to be compatible with podman, docker, or any other OCI complaint container engine.

Usage

To set up the template, replace ROS_DISTRO and REPO_NAME in Makefile with your desired distro and project name. If you're using devcontainers, replace ROS_DISTRO in .devcontainer.json as well.

Installing Packages

This template, by default, does not support ros-specific tooling for dependency or repository management. This is for several reasons, but the most important one is that we can only guarantee ros-specific tooling exists inside the container's ros environment. This means tools such as rosdep and vcstool must be ran on the workspace inside the container, necessitating the workspace be copied into the container as part of the base image. This would destroy our caching because any changes to the workspace would cause lengthy rebuilds.

The consequence of not supporting tools like rosdep inside the container is that, to install packages, you must manually find their apt keys and add them to the apt-get install block inside the base layer of the containerfile. Additionally, if you want to use the make final feature, you must add runtime dependencies to the apt-get install block inside the final layer.

To add source dependencies, use git submodules inside src. For example:

git submodule add https://github.com/ros2/examples.git src/examples

You can edit submodules in .gitmodules (full spec here), and manually sync them with make sync-submodules. By default, all the build commands will ensure the workspace's submodules are synced to .gitmodules and updated. If you want to pin submodules to specific versions, you should run git checkout <commit> from their directories.

Questions and Limitations

You only explain how to install packages from apt. What if I need dependencies that only exist in pypi?

To add additonal dependencies via pip, for example, make a new RUN command below that looks something like:

RUN pip install antigravity

Great, but what if I have multiple packages with conflicting versions of dependencies?

A constraint of this setup is that we are completely tied to one specific version of every ros and ubuntu package. Anything that doesn't fit those versions needs to be ported or can't be used.

If the messiness of dependency management bothers you as much as it bothered me, you may want to check out the real solution to these problems, nix. I will refrain from shilling too hard, but there exists nix-ros-overlay to enable working with ros packages from nix. There is also robostack, which is a possibly more mature solution that seeks to address similar problems for ros specifically.

Back to the question: Use conda inside a container at your own risk... I heard the last person who did it was found years later in a shack in the woods.

I don't use VSCode with devcontainers. How can I edit files with language server support from within the container with <insert my editor here>?

Ha

This specific issue is very annoying. So annoying that I wrote a blog post about it. In short, a fundamental flaw of container based development is that you, and any other contributors, will need to add their personal tooling to the dev stage in the containerfile, and not commit it or hope it doesn't conflict with other contributors'.

If this again bothers you, both nix and pixi (the tool that robostack is based on) fix this problem by not installing your dependencies inside a full isolated system from the host.

It sounds like you really dislike containers and container based development. Why did you make this?

This template only depends on a container engine, git, and make. These, unlike nix or pixi/conda, are tools almost everyone, regardless of their os, has installed on their system. Perhaps one day things may be different, but until then, I am but a servant to network effects.

About

a sane containerized ros setup using make

Resources

Stars

7 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

Repository files navigation

ros-template

This repository is a template for a containerized ROS 2 project. It uses make to implement the following utility commands:

CommandDescription
make buildBuilds the colcon workspace.
make testTests the colcon workspace.
make devEnters a bash shell inside the container containing the project's dependencies and build environment.
make finalBuilds the colcon workspace and copies the resulting artifacts into a minimal ros-core image tagged {name}:{distro}-final.
make refreshCauses the previous set of commands to rebuild the image when run.
make cleanUntags and removes any installed images.
make sync-submodulesManually syncs and recursively updates git submodules.

Why?

Working in ROS is often unnecessarily challenging due to the ecosystem relying on rosdep to map the dependency keys listed in projects' package.xml to those in linux distributions' repositories. This causes ROS distributions to be pinned to specific releases of specific linux distributions. Because individual ROS installations are often very complex and coupled to specific versions of ubuntu, it is often desirable to run the entire environment inside a docker container.

However, developing inside a containerized environment presents a very large pit of potential mistakes. Features like proper caching, x11 forwarding, a properly permissioned workspace volume, and even smaller things like an ergonomic development environment are all easy to mess up and cumbersome to implement.

Structure

This template project acts as the ros/colcon workspace, with all packages to be built going in src.

Containerfile contains build instructions for several stages of a container build, which can be used independently. Makefile contains the afformentioned scripts for using the container image. The images produced by the makefile's commands are:

  • {name}:{distro}-base (for make build), extends ros-{distro}-desktop-full
  • {name}:{distro}-dev (for make dev), extends base
  • {name}:{distro}-final (for make final), extends {distro}-ros-core

Where {name} is to be replaced with your project's name.

Make commands that rely on a specific image existing (make build, make test, make dev) will build the image first if it does not already exist. Afterwards, they will only rebuild the image if changes have been made to the Containerfile, or if they are forced to rebuild (make refresh, make clean). Since the images share most of the same layers, only your first command should take long, and the rest should be relatively quick. make build will also mount your entire project into /workspace, meaning build artifacts (build, install, log) are cached by default.

make dev will, by default, bridge the following between the host and container:

  • /workspace volume with rw access
  • host network
  • host user namespace
  • host pid namespace
  • host ipc namespace
  • X11

All the commands and syntax used is intended to be compatible with podman, docker, or any other OCI complaint container engine.

Usage

To set up the template, replace ROS_DISTRO and REPO_NAME in Makefile with your desired distro and project name. If you're using devcontainers, replace ROS_DISTRO in .devcontainer.json as well.

Installing Packages

This template, by default, does not support ros-specific tooling for dependency or repository management. This is for several reasons, but the most important one is that we can only guarantee ros-specific tooling exists inside the container's ros environment. This means tools such as rosdep and vcstool must be ran on the workspace inside the container, necessitating the workspace be copied into the container as part of the base image. This would destroy our caching because any changes to the workspace would cause lengthy rebuilds.

The consequence of not supporting tools like rosdep inside the container is that, to install packages, you must manually find their apt keys and add them to the apt-get install block inside the base layer of the containerfile. Additionally, if you want to use the make final feature, you must add runtime dependencies to the apt-get install block inside the final layer.

To add source dependencies, use git submodules inside src. For example:

git submodule add https://github.com/ros2/examples.git src/examples

You can edit submodules in .gitmodules (full spec here), and manually sync them with make sync-submodules. By default, all the build commands will ensure the workspace's submodules are synced to .gitmodules and updated. If you want to pin submodules to specific versions, you should run git checkout <commit> from their directories.

Questions and Limitations

You only explain how to install packages from apt. What if I need dependencies that only exist in pypi?

To add additonal dependencies via pip, for example, make a new RUN command below that looks something like:

RUN pip install antigravity

Great, but what if I have multiple packages with conflicting versions of dependencies?

A constraint of this setup is that we are completely tied to one specific version of every ros and ubuntu package. Anything that doesn't fit those versions needs to be ported or can't be used.

If the messiness of dependency management bothers you as much as it bothered me, you may want to check out the real solution to these problems, nix. I will refrain from shilling too hard, but there exists nix-ros-overlay to enable working with ros packages from nix. There is also robostack, which is a possibly more mature solution that seeks to address similar problems for ros specifically.

Back to the question: Use conda inside a container at your own risk... I heard the last person who did it was found years later in a shack in the woods.

I don't use VSCode with devcontainers. How can I edit files with language server support from within the container with <insert my editor here>?

Ha

This specific issue is very annoying. So annoying that I wrote a blog post about it. In short, a fundamental flaw of container based development is that you, and any other contributors, will need to add their personal tooling to the dev stage in the containerfile, and not commit it or hope it doesn't conflict with other contributors'.

If this again bothers you, both nix and pixi (the tool that robostack is based on) fix this problem by not installing your dependencies inside a full isolated system from the host.

It sounds like you really dislike containers and container based development. Why did you make this?

This template only depends on a container engine, git, and make. These, unlike nix or pixi/conda, are tools almost everyone, regardless of their os, has installed on their system. Perhaps one day things may be different, but until then, I am but a servant to network effects.

About

a sane containerized ros setup using make

Resources

Stars

7 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

ros-template

This repository is a template for a containerized ROS 2 project. It uses make to implement the following utility commands:

CommandDescription
make buildBuilds the colcon workspace.
make testTests the colcon workspace.
make devEnters a bash shell inside the container containing the project's dependencies and build environment.
make finalBuilds the colcon workspace and copies the resulting artifacts into a minimal ros-core image tagged {name}:{distro}-final.
make refreshCauses the previous set of commands to rebuild the image when run.
make cleanUntags and removes any installed images.
make sync-submodulesManually syncs and recursively updates git submodules.

Why?

Working in ROS is often unnecessarily challenging due to the ecosystem relying on rosdep to map the dependency keys listed in projects' package.xml to those in linux distributions' repositories. This causes ROS distributions to be pinned to specific releases of specific linux distributions. Because individual ROS installations are often very complex and coupled to specific versions of ubuntu, it is often desirable to run the entire environment inside a docker container.

However, developing inside a containerized environment presents a very large pit of potential mistakes. Features like proper caching, x11 forwarding, a properly permissioned workspace volume, and even smaller things like an ergonomic development environment are all easy to mess up and cumbersome to implement.

Structure

This template project acts as the ros/colcon workspace, with all packages to be built going in src.

Containerfile contains build instructions for several stages of a container build, which can be used independently. Makefile contains the afformentioned scripts for using the container image. The images produced by the makefile's commands are:

  • {name}:{distro}-base (for make build), extends ros-{distro}-desktop-full
  • {name}:{distro}-dev (for make dev), extends base
  • {name}:{distro}-final (for make final), extends {distro}-ros-core

Where {name} is to be replaced with your project's name.

Make commands that rely on a specific image existing (make build, make test, make dev) will build the image first if it does not already exist. Afterwards, they will only rebuild the image if changes have been made to the Containerfile, or if they are forced to rebuild (make refresh, make clean). Since the images share most of the same layers, only your first command should take long, and the rest should be relatively quick. make build will also mount your entire project into /workspace, meaning build artifacts (build, install, log) are cached by default.

make dev will, by default, bridge the following between the host and container:

  • /workspace volume with rw access
  • host network
  • host user namespace
  • host pid namespace
  • host ipc namespace
  • X11

All the commands and syntax used is intended to be compatible with podman, docker, or any other OCI complaint container engine.

Usage

To set up the template, replace ROS_DISTRO and REPO_NAME in Makefile with your desired distro and project name. If you're using devcontainers, replace ROS_DISTRO in .devcontainer.json as well.

Installing Packages

This template, by default, does not support ros-specific tooling for dependency or repository management. This is for several reasons, but the most important one is that we can only guarantee ros-specific tooling exists inside the container's ros environment. This means tools such as rosdep and vcstool must be ran on the workspace inside the container, necessitating the workspace be copied into the container as part of the base image. This would destroy our caching because any changes to the workspace would cause lengthy rebuilds.

The consequence of not supporting tools like rosdep inside the container is that, to install packages, you must manually find their apt keys and add them to the apt-get install block inside the base layer of the containerfile. Additionally, if you want to use the make final feature, you must add runtime dependencies to the apt-get install block inside the final layer.

To add source dependencies, use git submodules inside src. For example:

git submodule add https://github.com/ros2/examples.git src/examples

You can edit submodules in .gitmodules (full spec here), and manually sync them with make sync-submodules. By default, all the build commands will ensure the workspace's submodules are synced to .gitmodules and updated. If you want to pin submodules to specific versions, you should run git checkout <commit> from their directories.

Questions and Limitations

You only explain how to install packages from apt. What if I need dependencies that only exist in pypi?

To add additonal dependencies via pip, for example, make a new RUN command below that looks something like:

RUN pip install antigravity

Great, but what if I have multiple packages with conflicting versions of dependencies?

A constraint of this setup is that we are completely tied to one specific version of every ros and ubuntu package. Anything that doesn't fit those versions needs to be ported or can't be used.

If the messiness of dependency management bothers you as much as it bothered me, you may want to check out the real solution to these problems, nix. I will refrain from shilling too hard, but there exists nix-ros-overlay to enable working with ros packages from nix. There is also robostack, which is a possibly more mature solution that seeks to address similar problems for ros specifically.

Back to the question: Use conda inside a container at your own risk... I heard the last person who did it was found years later in a shack in the woods.

I don't use VSCode with devcontainers. How can I edit files with language server support from within the container with <insert my editor here>?

Ha

This specific issue is very annoying. So annoying that I wrote a blog post about it. In short, a fundamental flaw of container based development is that you, and any other contributors, will need to add their personal tooling to the dev stage in the containerfile, and not commit it or hope it doesn't conflict with other contributors'.

If this again bothers you, both nix and pixi (the tool that robostack is based on) fix this problem by not installing your dependencies inside a full isolated system from the host.

It sounds like you really dislike containers and container based development. Why did you make this?

This template only depends on a container engine, git, and make. These, unlike nix or pixi/conda, are tools almost everyone, regardless of their os, has installed on their system. Perhaps one day things may be different, but until then, I am but a servant to network effects.

About

a sane containerized ros setup using make

Resources

Stars

7 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

ros-template

This repository is a template for a containerized ROS 2 project. It uses make to implement the following utility commands:

CommandDescription
make buildBuilds the colcon workspace.
make testTests the colcon workspace.
make devEnters a bash shell inside the container containing the project's dependencies and build environment.
make finalBuilds the colcon workspace and copies the resulting artifacts into a minimal ros-core image tagged {name}:{distro}-final.
make refreshCauses the previous set of commands to rebuild the image when run.
make cleanUntags and removes any installed images.
make sync-submodulesManually syncs and recursively updates git submodules.

Why?

Working in ROS is often unnecessarily challenging due to the ecosystem relying on rosdep to map the dependency keys listed in projects' package.xml to those in linux distributions' repositories. This causes ROS distributions to be pinned to specific releases of specific linux distributions. Because individual ROS installations are often very complex and coupled to specific versions of ubuntu, it is often desirable to run the entire environment inside a docker container.

However, developing inside a containerized environment presents a very large pit of potential mistakes. Features like proper caching, x11 forwarding, a properly permissioned workspace volume, and even smaller things like an ergonomic development environment are all easy to mess up and cumbersome to implement.

Structure

This template project acts as the ros/colcon workspace, with all packages to be built going in src.

Containerfile contains build instructions for several stages of a container build, which can be used independently. Makefile contains the afformentioned scripts for using the container image. The images produced by the makefile's commands are:

  • {name}:{distro}-base (for make build), extends ros-{distro}-desktop-full
  • {name}:{distro}-dev (for make dev), extends base
  • {name}:{distro}-final (for make final), extends {distro}-ros-core

Where {name} is to be replaced with your project's name.

Make commands that rely on a specific image existing (make build, make test, make dev) will build the image first if it does not already exist. Afterwards, they will only rebuild the image if changes have been made to the Containerfile, or if they are forced to rebuild (make refresh, make clean). Since the images share most of the same layers, only your first command should take long, and the rest should be relatively quick. make build will also mount your entire project into /workspace, meaning build artifacts (build, install, log) are cached by default.

make dev will, by default, bridge the following between the host and container:

  • /workspace volume with rw access
  • host network
  • host user namespace
  • host pid namespace
  • host ipc namespace
  • X11

All the commands and syntax used is intended to be compatible with podman, docker, or any other OCI complaint container engine.

Usage

To set up the template, replace ROS_DISTRO and REPO_NAME in Makefile with your desired distro and project name. If you're using devcontainers, replace ROS_DISTRO in .devcontainer.json as well.

Installing Packages

This template, by default, does not support ros-specific tooling for dependency or repository management. This is for several reasons, but the most important one is that we can only guarantee ros-specific tooling exists inside the container's ros environment. This means tools such as rosdep and vcstool must be ran on the workspace inside the container, necessitating the workspace be copied into the container as part of the base image. This would destroy our caching because any changes to the workspace would cause lengthy rebuilds.

The consequence of not supporting tools like rosdep inside the container is that, to install packages, you must manually find their apt keys and add them to the apt-get install block inside the base layer of the containerfile. Additionally, if you want to use the make final feature, you must add runtime dependencies to the apt-get install block inside the final layer.

To add source dependencies, use git submodules inside src. For example:

git submodule add https://github.com/ros2/examples.git src/examples

You can edit submodules in .gitmodules (full spec here), and manually sync them with make sync-submodules. By default, all the build commands will ensure the workspace's submodules are synced to .gitmodules and updated. If you want to pin submodules to specific versions, you should run git checkout <commit> from their directories.

Questions and Limitations

You only explain how to install packages from apt. What if I need dependencies that only exist in pypi?

To add additonal dependencies via pip, for example, make a new RUN command below that looks something like:

RUN pip install antigravity

Great, but what if I have multiple packages with conflicting versions of dependencies?

A constraint of this setup is that we are completely tied to one specific version of every ros and ubuntu package. Anything that doesn't fit those versions needs to be ported or can't be used.

If the messiness of dependency management bothers you as much as it bothered me, you may want to check out the real solution to these problems, nix. I will refrain from shilling too hard, but there exists nix-ros-overlay to enable working with ros packages from nix. There is also robostack, which is a possibly more mature solution that seeks to address similar problems for ros specifically.

Back to the question: Use conda inside a container at your own risk... I heard the last person who did it was found years later in a shack in the woods.

I don't use VSCode with devcontainers. How can I edit files with language server support from within the container with <insert my editor here>?

Ha

This specific issue is very annoying. So annoying that I wrote a blog post about it. In short, a fundamental flaw of container based development is that you, and any other contributors, will need to add their personal tooling to the dev stage in the containerfile, and not commit it or hope it doesn't conflict with other contributors'.

If this again bothers you, both nix and pixi (the tool that robostack is based on) fix this problem by not installing your dependencies inside a full isolated system from the host.

It sounds like you really dislike containers and container based development. Why did you make this?

This template only depends on a container engine, git, and make. These, unlike nix or pixi/conda, are tools almost everyone, regardless of their os, has installed on their system. Perhaps one day things may be different, but until then, I am but a servant to network effects.

About

a sane containerized ros setup using make

Resources

Stars

7 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

Repository files navigation

ros-template

This repository is a template for a containerized ROS 2 project. It uses make to implement the following utility commands:

CommandDescription
make buildBuilds the colcon workspace.
make testTests the colcon workspace.
make devEnters a bash shell inside the container containing the project's dependencies and build environment.
make finalBuilds the colcon workspace and copies the resulting artifacts into a minimal ros-core image tagged {name}:{distro}-final.
make refreshCauses the previous set of commands to rebuild the image when run.
make cleanUntags and removes any installed images.
make sync-submodulesManually syncs and recursively updates git submodules.

Why?

Working in ROS is often unnecessarily challenging due to the ecosystem relying on rosdep to map the dependency keys listed in projects' package.xml to those in linux distributions' repositories. This causes ROS distributions to be pinned to specific releases of specific linux distributions. Because individual ROS installations are often very complex and coupled to specific versions of ubuntu, it is often desirable to run the entire environment inside a docker container.

However, developing inside a containerized environment presents a very large pit of potential mistakes. Features like proper caching, x11 forwarding, a properly permissioned workspace volume, and even smaller things like an ergonomic development environment are all easy to mess up and cumbersome to implement.

Structure

This template project acts as the ros/colcon workspace, with all packages to be built going in src.

Containerfile contains build instructions for several stages of a container build, which can be used independently. Makefile contains the afformentioned scripts for using the container image. The images produced by the makefile's commands are:

  • {name}:{distro}-base (for make build), extends ros-{distro}-desktop-full
  • {name}:{distro}-dev (for make dev), extends base
  • {name}:{distro}-final (for make final), extends {distro}-ros-core

Where {name} is to be replaced with your project's name.

Make commands that rely on a specific image existing (make build, make test, make dev) will build the image first if it does not already exist. Afterwards, they will only rebuild the image if changes have been made to the Containerfile, or if they are forced to rebuild (make refresh, make clean). Since the images share most of the same layers, only your first command should take long, and the rest should be relatively quick. make build will also mount your entire project into /workspace, meaning build artifacts (build, install, log) are cached by default.

make dev will, by default, bridge the following between the host and container:

  • /workspace volume with rw access
  • host network
  • host user namespace
  • host pid namespace
  • host ipc namespace
  • X11

All the commands and syntax used is intended to be compatible with podman, docker, or any other OCI complaint container engine.

Usage

To set up the template, replace ROS_DISTRO and REPO_NAME in Makefile with your desired distro and project name. If you're using devcontainers, replace ROS_DISTRO in .devcontainer.json as well.

Installing Packages

This template, by default, does not support ros-specific tooling for dependency or repository management. This is for several reasons, but the most important one is that we can only guarantee ros-specific tooling exists inside the container's ros environment. This means tools such as rosdep and vcstool must be ran on the workspace inside the container, necessitating the workspace be copied into the container as part of the base image. This would destroy our caching because any changes to the workspace would cause lengthy rebuilds.

The consequence of not supporting tools like rosdep inside the container is that, to install packages, you must manually find their apt keys and add them to the apt-get install block inside the base layer of the containerfile. Additionally, if you want to use the make final feature, you must add runtime dependencies to the apt-get install block inside the final layer.

To add source dependencies, use git submodules inside src. For example:

git submodule add https://github.com/ros2/examples.git src/examples

You can edit submodules in .gitmodules (full spec here), and manually sync them with make sync-submodules. By default, all the build commands will ensure the workspace's submodules are synced to .gitmodules and updated. If you want to pin submodules to specific versions, you should run git checkout <commit> from their directories.

Questions and Limitations

You only explain how to install packages from apt. What if I need dependencies that only exist in pypi?

To add additonal dependencies via pip, for example, make a new RUN command below that looks something like:

RUN pip install antigravity

Great, but what if I have multiple packages with conflicting versions of dependencies?

A constraint of this setup is that we are completely tied to one specific version of every ros and ubuntu package. Anything that doesn't fit those versions needs to be ported or can't be used.

If the messiness of dependency management bothers you as much as it bothered me, you may want to check out the real solution to these problems, nix. I will refrain from shilling too hard, but there exists nix-ros-overlay to enable working with ros packages from nix. There is also robostack, which is a possibly more mature solution that seeks to address similar problems for ros specifically.

Back to the question: Use conda inside a container at your own risk... I heard the last person who did it was found years later in a shack in the woods.

I don't use VSCode with devcontainers. How can I edit files with language server support from within the container with <insert my editor here>?

Ha

This specific issue is very annoying. So annoying that I wrote a blog post about it. In short, a fundamental flaw of container based development is that you, and any other contributors, will need to add their personal tooling to the dev stage in the containerfile, and not commit it or hope it doesn't conflict with other contributors'.

If this again bothers you, both nix and pixi (the tool that robostack is based on) fix this problem by not installing your dependencies inside a full isolated system from the host.

It sounds like you really dislike containers and container based development. Why did you make this?

This template only depends on a container engine, git, and make. These, unlike nix or pixi/conda, are tools almost everyone, regardless of their os, has installed on their system. Perhaps one day things may be different, but until then, I am but a servant to network effects.

About

a sane containerized ros setup using make

Resources

Stars

7 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages