feat: C/C++ providers (#319)

* Start working on ROS provider

* CMake provider, get ROS at least doing something

* update snapshots

* ROS builds now

* update snapshots

* ROS (I think) works

* Add Meson provider

* Add CMake docs

* fix cpp-cmake example

* fix docs build

* Add Meson docs

* ROS docs, allow specifying launch file

* Merge cmake and meson into C++ provider

* Merge docs

* UseMiseVersions()

* trigger ci

* Remove ROS provider

* fix: add cleanse implementation

* test: snapshot update

* test: missing py snapshot update

---------

Co-authored-by: Michael Bianco <[email protected]>
This commit is contained in:
Aleks Rūtiņš
2025-11-20 16:06:15 -07:00
committed by GitHub
co-authored by Michael Bianco
parent ac447bc76f
commit 0bff0ecf6c
16 changed files with 336 additions and 1 deletions
@@ -0,0 +1,82 @@
{
"deploy": {
"base": {
"image": "ghcr.io/railwayapp/railpack-runtime:latest"
},
"inputs": [
{
"include": [
"/build/"
],
"step": "build"
}
],
"startCommand": "/build/cpp-cmake"
},
"steps": [
{
"assets": {
"mise.toml": "[mise.toml]"
},
"commands": [
{
"path": "/mise/shims"
},
{
"customName": "create mise config",
"name": "mise.toml",
"path": "/etc/mise/config.toml"
},
{
"cmd": "mise install",
"customName": "install mise packages: cmake, ninja"
}
],
"inputs": [
{
"image": "ghcr.io/railwayapp/railpack-builder:latest"
}
],
"name": "packages:mise",
"variables": {
"MISE_CACHE_DIR": "/mise/cache",
"MISE_CONFIG_DIR": "/mise",
"MISE_DATA_DIR": "/mise",
"MISE_IDIOMATIC_VERSION_FILE_ENABLE_TOOLS": "python,node,ruby,elixir,go,java,yarn",
"MISE_INSTALLS_DIR": "/mise/installs",
"MISE_NODE_VERIFY": "false",
"MISE_PARANOID": "1",
"MISE_SHIMS_DIR": "/mise/shims",
"MISE_TRUSTED_CONFIG_PATHS": "/app"
}
},
{
"commands": [
{
"cmd": "mkdir /build"
},
{
"cmd": "cmake -B /build -GNinja /app"
},
{
"cmd": "cmake --build /build"
}
],
"inputs": [
{
"step": "packages:mise"
},
{
"include": [
"."
],
"local": true
}
],
"name": "build",
"secrets": [
"*"
]
}
]
}
@@ -0,0 +1,82 @@
{
"deploy": {
"base": {
"image": "ghcr.io/railwayapp/railpack-runtime:latest"
},
"inputs": [
{
"include": [
"/build/"
],
"step": "build"
}
],
"startCommand": "/build/cpp-meson"
},
"steps": [
{
"assets": {
"mise.toml": "[mise.toml]"
},
"commands": [
{
"path": "/mise/shims"
},
{
"customName": "create mise config",
"name": "mise.toml",
"path": "/etc/mise/config.toml"
},
{
"cmd": "mise install",
"customName": "install mise packages: meson, ninja"
}
],
"inputs": [
{
"image": "ghcr.io/railwayapp/railpack-builder:latest"
}
],
"name": "packages:mise",
"variables": {
"MISE_CACHE_DIR": "/mise/cache",
"MISE_CONFIG_DIR": "/mise",
"MISE_DATA_DIR": "/mise",
"MISE_IDIOMATIC_VERSION_FILE_ENABLE_TOOLS": "python,node,ruby,elixir,go,java,yarn",
"MISE_INSTALLS_DIR": "/mise/installs",
"MISE_NODE_VERIFY": "false",
"MISE_PARANOID": "1",
"MISE_SHIMS_DIR": "/mise/shims",
"MISE_TRUSTED_CONFIG_PATHS": "/app"
}
},
{
"commands": [
{
"cmd": "mkdir /build"
},
{
"cmd": "meson setup /build"
},
{
"cmd": "meson compile -C /build"
}
],
"inputs": [
{
"step": "packages:mise"
},
{
"include": [
"."
],
"local": true
}
],
"name": "build",
"secrets": [
"*"
]
}
]
}
@@ -111,7 +111,8 @@
"MISE_NODE_VERIFY": "false",
"MISE_PARANOID": "1",
"MISE_PYTHON_COMPILE": "false",
"MISE_SHIMS_DIR": "/mise/shims"
"MISE_SHIMS_DIR": "/mise/shims",
"MISE_TRUSTED_CONFIG_PATHS": "/app"
}
},
{
+8
View File
@@ -0,0 +1,8 @@
package cpp
import "github.com/railwayapp/railpack/core/generate"
type buildSystem interface {
Install(ctx *generate.GenerateContext, pkgs *generate.MiseStepBuilder)
Build(build *generate.CommandStepBuilder)
}
+28
View File
@@ -0,0 +1,28 @@
package cpp
import (
"github.com/railwayapp/railpack/core/generate"
"github.com/railwayapp/railpack/core/plan"
)
type cmake struct{}
func (_ *CppProvider) DetectCmake(ctx *generate.GenerateContext) (buildSystem, bool) {
if ctx.App.HasFile("CMakeLists.txt") {
return &cmake{}, true
}
return nil, false
}
func (_ *cmake) Install(ctx *generate.GenerateContext, mise *generate.MiseStepBuilder) {
mise.Default("cmake", "latest")
mise.Default("ninja", "latest")
mise.UseMiseVersions(ctx, []string{"meson", "ninja"})
}
func (_ *cmake) Build(build *generate.CommandStepBuilder) {
build.AddCommands([]plan.Command{
plan.NewExecCommand("cmake -B /build -GNinja /app"),
plan.NewExecCommand("cmake --build /build"),
})
}
+56
View File
@@ -0,0 +1,56 @@
package cpp
import (
"fmt"
"path/filepath"
"github.com/railwayapp/railpack/core/generate"
"github.com/railwayapp/railpack/core/plan"
)
type CppProvider struct{}
func (p *CppProvider) Name() string {
return "cpp"
}
func (p *CppProvider) CleansePlan(buildPlan *plan.BuildPlan) {}
func (p *CppProvider) Detect(ctx *generate.GenerateContext) (bool, error) {
_, c := p.DetectCmake(ctx)
_, m := p.DetectMeson(ctx)
return c || m, nil
}
func (p *CppProvider) Initialize(ctx *generate.GenerateContext) error {
return nil
}
func (p *CppProvider) StartCommandHelp() string {
return ""
}
func (p *CppProvider) Plan(ctx *generate.GenerateContext) error {
packages := ctx.GetMiseStepBuilder()
buildsystem, found := p.DetectCmake(ctx)
if !found {
buildsystem, _ = p.DetectMeson(ctx)
}
buildsystem.Install(ctx, packages)
build := ctx.NewCommandStep("build")
build.AddInput(plan.NewStepLayer(packages.Name()))
build.AddInput(ctx.NewLocalLayer())
build.AddCommand(plan.NewExecCommand("mkdir /build"))
buildsystem.Build(build)
ctx.Deploy.StartCmd = fmt.Sprintf("/build/%s", filepath.Base(ctx.GetAppSource()))
ctx.Deploy.AddInputs([]plan.Layer{
plan.NewStepLayer(build.Name(), plan.NewIncludeFilter([]string{"/build/"})),
})
return nil
}
+28
View File
@@ -0,0 +1,28 @@
package cpp
import (
"github.com/railwayapp/railpack/core/generate"
"github.com/railwayapp/railpack/core/plan"
)
type meson struct{}
func (_ *CppProvider) DetectMeson(ctx *generate.GenerateContext) (buildSystem, bool) {
if ctx.App.HasFile("meson.build") {
return &meson{}, true
}
return nil, false
}
func (_ *meson) Install(ctx *generate.GenerateContext, mise *generate.MiseStepBuilder) {
mise.Default("meson", "latest")
mise.Default("ninja", "latest")
mise.UseMiseVersions(ctx, []string{"meson", "ninja"})
}
func (_ *meson) Build(build *generate.CommandStepBuilder) {
build.AddCommands([]plan.Command{
plan.NewExecCommand("meson setup /build"),
plan.NewExecCommand("meson compile -C /build"),
})
}
+2
View File
@@ -3,6 +3,7 @@ package providers
import (
"github.com/railwayapp/railpack/core/generate"
"github.com/railwayapp/railpack/core/plan"
"github.com/railwayapp/railpack/core/providers/cpp"
"github.com/railwayapp/railpack/core/providers/deno"
"github.com/railwayapp/railpack/core/providers/dotnet"
"github.com/railwayapp/railpack/core/providers/elixir"
@@ -41,6 +42,7 @@ func GetLanguageProviders() []Provider {
&dotnet.DotnetProvider{},
&node.NodeProvider{},
&gleam.GleamProvider{},
&cpp.CppProvider{},
&staticfile.StaticfileProvider{},
&shell.ShellProvider{},
}
+1
View File
@@ -144,6 +144,7 @@ export default defineConfig({
{ label: "Rust", link: "/languages/rust" },
{ label: "Elixir", link: "/languages/elixir" },
{ label: "Gleam", link: "/languages/gleam" },
{ label: "C/C++", link: "/languages/cpp" },
{ label: "Staticfile", link: "/languages/staticfile" },
{ label: "Shell Scripts", link: "/languages/shell" },
],
+18
View File
@@ -0,0 +1,18 @@
---
title: C/C++
description: Building C/C++ applications with Railpack
---
Railpack builds and deploys CMake- and Meson-based C/C++ applications with zero configuration.
## Detection
Your project will be detected as a C/C++ application if a `CMakeLists.txt` file or a `meson.build` file exists in the root directory.
## Versions
The latest versions of CMake (or Meson) and Ninja will be installed during build.
## Configuration
Railpack will build your application into a build directory at `/build`, and run the executable in that directory whose name matches the name of your project's root directory. The source tree will not be available in the final container by default, only the build directory.
+4
View File
@@ -0,0 +1,4 @@
cmake_minimum_required(VERSION 3.15)
project(cpp-cmake LANGUAGES CXX)
add_executable(cpp-cmake main.cc)
+6
View File
@@ -0,0 +1,6 @@
#include <iostream>
int main() {
std::cout << "Hello from C++" << std::endl;
return 0;
}
+5
View File
@@ -0,0 +1,5 @@
[
{
"expectedOutput": "Hello from C++"
}
]
+6
View File
@@ -0,0 +1,6 @@
#include <iostream>
int main() {
std::cout << "Hello from C++" << std::endl;
return 0;
}
+3
View File
@@ -0,0 +1,3 @@
project('cpp-meson', ['cpp'])
executable('cpp-meson', ['main.cc'])
+5
View File
@@ -0,0 +1,5 @@
[
{
"expectedOutput": "Hello from C++"
}
]