This is an automated email from the ASF dual-hosted git repository.
davsclaus pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel.git
The following commit(s) were added to refs/heads/main by this push:
new d603bdd93625 CAMEL-23703: Add native x64 camel.exe bootstrap to
camel-launcher Windows distribution
d603bdd93625 is described below
commit d603bdd93625d0dc2a13e1efcde6f69b6dcbc3ba
Author: Adriano Machado <[email protected]>
AuthorDate: Wed Jul 15 03:00:52 2026 -0400
CAMEL-23703: Add native x64 camel.exe bootstrap to camel-launcher Windows
distribution
Adds a native x64 bin/camel.exe to the camel-launcher Windows distribution
alongside
bin/camel.bat. The executable is a thin C bootstrap (~130 lines) that
locates the adjacent
camel.bat, forwards all arguments verbatim (preserving spaces and Unicode),
and returns
the child exit code. It exists so package managers requiring a genuine
executable (e.g.
WinGet) can expose camel directly.
Native build logic lives in a new standalone module tooling/camel-exe with
MSVC Maven
profile, behavioral tests (Windows-only), and a release gate property. A
dedicated CI
workflow builds and tests camel.exe on Windows x64.
Closes #24665
---
.github/workflows/camel-launcher-windows.yml | 133 +++++++++++++
.../ROOT/pages/camel-4x-upgrade-guide-4_22.adoc | 8 +
dsl/camel-jbang/camel-launcher/README.md | 20 ++
dsl/camel-jbang/camel-launcher/pom.xml | 106 ++++++++++
.../camel-launcher/src/main/assembly/bin.xml | 8 +
.../jbang/launcher/CamelLauncherWindowsExeIT.java | 77 ++++++++
tooling/camel-exe/README.md | 45 +++++
tooling/camel-exe/pom.xml | 214 ++++++++++++++++++++
tooling/camel-exe/src/main/native/README.md | 26 +++
tooling/camel-exe/src/main/native/camel.c | 146 ++++++++++++++
.../camel/tooling/exe/CamelExeBootstrapTest.java | 217 +++++++++++++++++++++
.../java/org/apache/camel/maven/RepackageMojo.java | 8 +-
.../org/apache/camel/maven/RepackageMojoTest.java | 45 +++++
tooling/pom.xml | 1 +
14 files changed, 1053 insertions(+), 1 deletion(-)
diff --git a/.github/workflows/camel-launcher-windows.yml
b/.github/workflows/camel-launcher-windows.yml
new file mode 100644
index 000000000000..d6a9cca15176
--- /dev/null
+++ b/.github/workflows/camel-launcher-windows.yml
@@ -0,0 +1,133 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+name: Camel launcher Windows build
+
+on:
+ push:
+ branches:
+ - main
+ pull_request:
+ branches:
+ - main
+ workflow_dispatch:
+
+concurrency:
+ group: ${{ github.workflow }}-${{ github.ref }}
+ cancel-in-progress: true
+
+permissions:
+ contents: read
+
+jobs:
+ detect-changes:
+ runs-on: ubuntu-latest
+ outputs:
+ camel-exe: ${{ steps.changes.outputs.camel-exe }}
+ camel-launcher: ${{ steps.changes.outputs.camel-launcher }}
+ steps:
+ - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 #
v7.0.0
+ with:
+ persist-credentials: false
+ # 50 commits covers any reasonable push batch; PRs fetch the base
ref below.
+ fetch-depth: 50
+ - id: changes
+ run: |
+ if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
+ echo "camel-exe=true" >> "$GITHUB_OUTPUT"
+ echo "camel-launcher=true" >> "$GITHUB_OUTPUT"
+ exit 0
+ fi
+
+ if [ "${{ github.event_name }}" = "pull_request" ]; then
+ git fetch origin "${{ github.base_ref }}" --depth=1
+ CHANGED="$(git diff --name-only "origin/${{ github.base_ref
}}"...HEAD)"
+ else
+ CHANGED="$(git diff --name-only "${{ github.event.before }}" "${{
github.sha }}")"
+ fi
+
+ echo "Changed files:"
+ echo "$CHANGED"
+
+ camel_exe=false
+ camel_launcher=false
+
+ if echo "$CHANGED" | grep -qE
'^(tooling/camel-exe/|\.github/workflows/camel-launcher-windows\.yml)'; then
+ camel_exe=true
+ fi
+
+ if echo "$CHANGED" | grep -qE
'^(dsl/camel-jbang/camel-launcher/|tooling/camel-exe/|\.github/workflows/camel-launcher-windows\.yml)';
then
+ camel_launcher=true
+ fi
+
+ echo "camel-exe=$camel_exe" >> "$GITHUB_OUTPUT"
+ echo "camel-launcher=$camel_launcher" >> "$GITHUB_OUTPUT"
+
+ camel-exe:
+ needs: detect-changes
+ if: github.event_name == 'workflow_dispatch' ||
needs.detect-changes.outputs.camel-exe == 'true'
+ runs-on: windows-2022
+ steps:
+ - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 #
v7.0.0
+ with:
+ persist-credentials: false
+ - name: Set up JDK 21
+ uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 #
v5.2.0
+ with:
+ distribution: 'temurin'
+ java-version: '21'
+ cache: 'maven'
+ - name: Build and test native camel.exe
+ shell: cmd
+ run: |
+ for /f "usebackq delims=" %%i in (`"%ProgramFiles(x86)%\Microsoft
Visual Studio\Installer\vswhere.exe" -latest -products * -requires
Microsoft.VisualStudio.Component.VC.Tools.x86.x64 -property installationPath`)
do set "VSINSTALL=%%i"
+ call "%VSINSTALL%\VC\Auxiliary\Build\vcvars64.bat"
+ mvn -B -ntp -pl buildingtools install -DskipTests
+ mvn -B -ntp -pl tooling/camel-exe verify
-Dcamel.exe.requireWindowsExe=true
+
+ camel-launcher-windows:
+ needs: detect-changes
+ if: github.event_name == 'workflow_dispatch' ||
needs.detect-changes.outputs.camel-launcher == 'true'
+ runs-on: windows-2022
+ steps:
+ - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 #
v7.0.0
+ with:
+ persist-credentials: false
+ - name: Set up JDK 21
+ uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 #
v5.2.0
+ with:
+ distribution: 'temurin'
+ java-version: '21'
+ cache: 'maven'
+ - name: Build launcher with native camel.exe
+ shell: cmd
+ run: |
+ for /f "usebackq delims=" %%i in (`"%ProgramFiles(x86)%\Microsoft
Visual Studio\Installer\vswhere.exe" -latest -products * -requires
Microsoft.VisualStudio.Component.VC.Tools.x86.x64 -property installationPath`)
do set "VSINSTALL=%%i"
+ call "%VSINSTALL%\VC\Auxiliary\Build\vcvars64.bat"
+ mvn -B -ntp -pl
buildingtools,tooling/camel-exe,dsl/camel-jbang/camel-launcher -am install
-DskipTests -Dcamel.launcher.requireWindowsExe=true
+ - name: Assert archive carries bin/camel.exe
+ shell: pwsh
+ run: |
+ $zip = Get-ChildItem
dsl/camel-jbang/camel-launcher/target/camel-launcher-*-bin.zip | Select-Object
-First 1
+ Add-Type -AssemblyName System.IO.Compression.FileSystem
+ $archive = [System.IO.Compression.ZipFile]::OpenRead($zip.FullName)
+ try {
+ $names = $archive.Entries.FullName
+ if (-not ($names -like '*/bin/camel.exe')) { throw 'camel.exe
missing from launcher archive' }
+ } finally {
+ $archive.Dispose()
+ }
diff --git
a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_22.adoc
b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_22.adoc
index 0454ac8a835a..e58517c676e4 100644
--- a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_22.adoc
+++ b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_22.adoc
@@ -80,6 +80,14 @@ The Gentoo `java-config` auto-detection and the IBM AIX
`$JAVA_HOME/jre/sh/java`
removed; set `JAVA_HOME` or `JAVACMD` explicitly on those platforms.
`JAVA_OPTS` handling is unchanged: when unset, the default `-Xmx512m` heap is
applied.
+==== Native `camel.exe` in the launcher distribution
+
+The `camel-launcher` Windows distribution now ships a native x64
`bin/camel.exe`
+alongside `bin/camel.bat`. `camel.exe` is a thin bootstrap: it forwards all
+arguments to the adjacent `camel.bat` (preserving spaces and Unicode) and
returns
+its exit code. It exists so package managers that require a genuine executable
+users may continue to invoke `bin\camel.bat`; both behave identically.
+
=== camel-langchain4j-agent
The `Agent.chat()` method return type has changed from `String` to
`Result<String>` (from `dev.langchain4j.service.Result`).
diff --git a/dsl/camel-jbang/camel-launcher/README.md
b/dsl/camel-jbang/camel-launcher/README.md
index 06e2f1dd63cb..845c8b894fe5 100644
--- a/dsl/camel-jbang/camel-launcher/README.md
+++ b/dsl/camel-jbang/camel-launcher/README.md
@@ -16,6 +16,22 @@ This will create:
1. A self-executing JAR (`camel-launcher-<version>.jar`) in the `target`
directory using Spring Boot's nested JAR structure
2. Distribution archives (`camel-launcher-<version>-bin.zip` and
`camel-launcher-<version>-bin.tar.gz`) in the `target` directory
+On Windows, the distribution archives also include `bin/camel.exe`, a native
bootstrap built by
+[`tooling/camel-exe`](../../../tooling/camel-exe). Release builds on a Windows
x64 host run an integration test during `verify` that asserts
+`target/camel.exe` is staged and `bin/camel.exe` is present in the assembled
ZIP:
+
+```bash
+mvn -pl tooling/camel-exe,dsl/camel-jbang/camel-launcher -am verify
-Dcamel.launcher.requireWindowsExe=true
+```
+
+To build and test only the native bootstrap:
+
+```bash
+mvn -pl tooling/camel-exe verify -Dcamel.exe.requireWindowsExe=true
+```
+
+See
[`tooling/camel-exe/src/main/native/README.md`](../../../tooling/camel-exe/src/main/native/README.md)
for MSVC requirements.
+
## Usage
### Using the JAR directly
@@ -47,8 +63,12 @@ java -jar camel-launcher-<version>.jar run hello.java
# On Windows
bin\camel.bat [command] [options]
+ bin\camel.exe [command] [options]
```
+ `camel.exe` and `camel.bat` behave identically on Windows; `camel.exe`
exists for package managers
+ (such as WinGet) that require a genuine executable command.
+
## Benefits
- No need for JBang installation
diff --git a/dsl/camel-jbang/camel-launcher/pom.xml
b/dsl/camel-jbang/camel-launcher/pom.xml
index 3694faf67e92..6668d00051cd 100644
--- a/dsl/camel-jbang/camel-launcher/pom.xml
+++ b/dsl/camel-jbang/camel-launcher/pom.xml
@@ -325,4 +325,110 @@
</plugin>
</plugins>
</build>
+
+ <profiles>
+ <!--
+ On Windows, copy the native camel.exe artifact from
tooling/camel-exe into target/
+ so the assembly descriptor can include it in bin/camel.exe.
+ -->
+ <profile>
+ <id>include-camel-exe</id>
+ <activation>
+ <os>
+ <family>windows</family>
+ </os>
+ </activation>
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.camel</groupId>
+ <artifactId>camel-exe</artifactId>
+ <version>${project.version}</version>
+ <type>exe</type>
+ </dependency>
+ </dependencies>
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-dependency-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>copy-camel-exe</id>
+ <phase>generate-resources</phase>
+ <goals>
+ <goal>copy</goal>
+ </goals>
+ <configuration>
+ <artifactItems>
+ <artifactItem>
+ <groupId>org.apache.camel</groupId>
+ <artifactId>camel-exe</artifactId>
+
<version>${project.version}</version>
+ <type>exe</type>
+
<outputDirectory>${project.build.directory}</outputDirectory>
+
<destFileName>camel.exe</destFileName>
+ </artifactItem>
+ </artifactItems>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+ </profile>
+
+ <!--
+ Release gate: fails the build before packaging if camel.exe was
not copied into
+ target/. Enabled only when -Dcamel.launcher.requireWindowsExe=true
(release job on
+ a Windows x64 host), so ordinary cross-platform dev builds are
unaffected.
+ -->
+ <profile>
+ <id>require-windows-exe</id>
+ <activation>
+ <property>
+ <name>camel.launcher.requireWindowsExe</name>
+ <value>true</value>
+ </property>
+ </activation>
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-enforcer-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>require-camel-exe</id>
+ <phase>prepare-package</phase>
+ <goals>
+ <goal>enforce</goal>
+ </goals>
+ <configuration>
+ <rules>
+ <requireFilesExist>
+ <files>
+
<file>${project.build.directory}/camel.exe</file>
+ </files>
+ </requireFilesExist>
+ </rules>
+ <fail>true</fail>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-failsafe-plugin</artifactId>
+ <executions>
+ <execution>
+ <goals>
+ <goal>integration-test</goal>
+ <goal>verify</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+ </profile>
+ </profiles>
</project>
diff --git a/dsl/camel-jbang/camel-launcher/src/main/assembly/bin.xml
b/dsl/camel-jbang/camel-launcher/src/main/assembly/bin.xml
index 32432cac0ccb..7506708d80a9 100644
--- a/dsl/camel-jbang/camel-launcher/src/main/assembly/bin.xml
+++ b/dsl/camel-jbang/camel-launcher/src/main/assembly/bin.xml
@@ -70,5 +70,13 @@
<include>*.*</include>
</includes>
</fileSet>
+ <fileSet>
+ <directory>${project.build.directory}</directory>
+ <outputDirectory>bin</outputDirectory>
+ <includes>
+ <include>camel.exe</include>
+ </includes>
+ <fileMode>0755</fileMode>
+ </fileSet>
</fileSets>
</assembly>
diff --git
a/dsl/camel-jbang/camel-launcher/src/test/java/org/apache/camel/dsl/jbang/launcher/CamelLauncherWindowsExeIT.java
b/dsl/camel-jbang/camel-launcher/src/test/java/org/apache/camel/dsl/jbang/launcher/CamelLauncherWindowsExeIT.java
new file mode 100644
index 000000000000..c2108f78fa20
--- /dev/null
+++
b/dsl/camel-jbang/camel-launcher/src/test/java/org/apache/camel/dsl/jbang/launcher/CamelLauncherWindowsExeIT.java
@@ -0,0 +1,77 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.dsl.jbang.launcher;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.stream.Stream;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
+
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.condition.EnabledIfSystemProperty;
+import org.junit.jupiter.api.condition.EnabledOnOs;
+import org.junit.jupiter.api.condition.OS;
+
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+/**
+ * Release gate for the native {@code camel.exe} packaged into the launcher
distribution. Runs during {@code verify}
+ * when {@code -Dcamel.launcher.requireWindowsExe=true} is set on a Windows
x64 host.
+ */
+@EnabledOnOs(OS.WINDOWS)
+@EnabledIfSystemProperty(named = "camel.launcher.requireWindowsExe", matches =
"true")
+class CamelLauncherWindowsExeIT {
+
+ private static final Path TARGET = Paths.get("target");
+ private static final Path STAGED_EXE = TARGET.resolve("camel.exe");
+
+ @Test
+ void stagedCamelExeExists() {
+ assertTrue(Files.isRegularFile(STAGED_EXE),
+ "target/camel.exe must be present before packaging the
launcher distribution");
+ }
+
+ @Test
+ void binArchiveIncludesCamelExe() throws IOException {
+ Path zip = findBinZip();
+ assertNotNull(zip, "camel-launcher-*-bin.zip must be produced by the
assembly plugin");
+
+ try (ZipFile archive = new ZipFile(zip.toFile())) {
+ // maven-assembly-plugin defaults includeBaseDirectory to true, so
entries are
+ // nested under camel-launcher-<version>/, not at the archive root.
+ ZipEntry entry = archive.stream()
+ .filter(e -> e.getName().endsWith("/bin/camel.exe"))
+ .findFirst()
+ .orElse(null);
+ assertNotNull(entry, "bin/camel.exe must be included in " +
zip.getFileName());
+ assertTrue(entry.getSize() > 0, "bin/camel.exe must not be empty");
+ }
+ }
+
+ private static Path findBinZip() throws IOException {
+ try (Stream<Path> paths = Files.list(TARGET)) {
+ return paths
+ .filter(p ->
p.getFileName().toString().matches("camel-launcher-.*-bin\\.zip"))
+ .findFirst()
+ .orElse(null);
+ }
+ }
+}
diff --git a/tooling/camel-exe/README.md b/tooling/camel-exe/README.md
new file mode 100644
index 000000000000..390463492995
--- /dev/null
+++ b/tooling/camel-exe/README.md
@@ -0,0 +1,45 @@
+# Camel :: Exe
+
+Standalone Maven module that builds the native Windows **`camel.exe`**
bootstrap for the
+[Camel CLI launcher](../../dsl/camel-jbang/camel-launcher).
+
+## Purpose
+
+WinGet's portable installer (and similar package managers) require a genuine
executable as
+the `camel` command. A `.bat` or `.cmd` shim is not enough — it produces a
broken
+`camel.exe` symlink. This module compiles a minimal x64 native binary that
satisfies that
+contract.
+
+`camel.exe` does not embed Java or run Camel itself. It locates `camel.bat` in
the same
+directory, forwards the caller's command line (preserving spaces and Unicode),
and returns
+its exit code. All Java discovery and CLI parsing remain in `camel.bat` and
the launcher
+JAR.
+
+## Why a separate module?
+
+The launcher (`dsl/camel-jbang/camel-launcher`) is a fat JAR with a large
upstream
+dependency tree (jbang core, plugins, repackager, and their transitive Camel
deps). The
+native bootstrap is ~100 lines of C with **no Java dependencies**. Keeping it
here allows:
+
+- **Fast Windows CI** — `mvn -pl tooling/camel-exe verify` compiles and tests
the exe
+ without building the full jbang graph.
+- **Clear separation** — packaging bootstrap vs. runtime launcher.
+- **Reusable artifact** — `camel-launcher` copies the attached `exe` artifact
into its
+ distribution on Windows (`bin/camel.exe` inside `camel-launcher-*-bin.zip`).
+
+## Build and test
+
+On a Windows x64 host with Microsoft C/C++ Build Tools (`cl.exe`) on PATH:
+
+```bash
+mvn -pl tooling/camel-exe verify -Dcamel.exe.requireWindowsExe=true
+```
+
+Release and integration builds that produce the launcher ZIP also build this
module first:
+
+```bash
+mvn -pl tooling/camel-exe,dsl/camel-jbang/camel-launcher -am verify
-Dcamel.launcher.requireWindowsExe=true
+```
+
+See [src/main/native/README.md](src/main/native/README.md) for MSVC setup,
compiler
+flags, and the release-gate profile.
diff --git a/tooling/camel-exe/pom.xml b/tooling/camel-exe/pom.xml
new file mode 100644
index 000000000000..f05e18ea80f5
--- /dev/null
+++ b/tooling/camel-exe/pom.xml
@@ -0,0 +1,214 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
+
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>org.apache.camel</groupId>
+ <artifactId>tooling</artifactId>
+ <version>4.22.0-SNAPSHOT</version>
+ </parent>
+
+ <artifactId>camel-exe</artifactId>
+ <packaging>pom</packaging>
+
+ <name>Camel :: Exe</name>
+ <description>Native Windows camel.exe bootstrap for the Camel CLI
launcher</description>
+
+ <properties>
+ <camel-prepare-component>false</camel-prepare-component>
+ <label>tooling</label>
+ </properties>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.junit.jupiter</groupId>
+ <artifactId>junit-jupiter</artifactId>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+
+ <!--
+ POM packaging is used because the main artifact is a native .exe, not
a jar.
+ The test plugins below are wired manually so that
CamelExeBootstrapTest (Java,
+ Windows-only) runs during the standard 'mvn verify' lifecycle.
+ -->
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.codehaus.mojo</groupId>
+ <artifactId>build-helper-maven-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>add-test-source</id>
+ <phase>generate-test-sources</phase>
+ <goals>
+ <goal>add-test-source</goal>
+ </goals>
+ <configuration>
+ <sources>
+ <source>src/test/java</source>
+ </sources>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-compiler-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>default-testCompile</id>
+ <phase>test-compile</phase>
+ <goals>
+ <goal>testCompile</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-surefire-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>default-test</id>
+ <phase>test</phase>
+ <goals>
+ <goal>test</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+
+ <profiles>
+ <!--
+ Compiles the native x64 camel.exe bootstrap with the Microsoft
C/C++ Build Tools.
+ Activates automatically on Windows; requires cl.exe on PATH (run
from a developer
+ command prompt / after vcvars64.bat). Non-Windows builds skip this
and omit camel.exe.
+ -->
+ <profile>
+ <id>build-windows-exe</id>
+ <activation>
+ <os>
+ <family>windows</family>
+ </os>
+ </activation>
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.codehaus.mojo</groupId>
+ <artifactId>exec-maven-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>compile-camel-exe</id>
+ <phase>generate-test-resources</phase>
+ <goals>
+ <goal>exec</goal>
+ </goals>
+ <configuration>
+ <executable>cl.exe</executable>
+ <arguments>
+ <argument>/nologo</argument>
+ <argument>/W4</argument>
+ <argument>/O1</argument>
+ <argument>/MT</argument>
+ <argument>/Brepro</argument>
+
<argument>/Fe:${project.build.directory}\camel.exe</argument>
+
<argument>/Fo:${project.build.directory}\camel.obj</argument>
+
<argument>${project.basedir}\src\main\native\camel.c</argument>
+ <argument>/link</argument>
+ <argument>/Brepro</argument>
+ <argument>/SUBSYSTEM:CONSOLE</argument>
+ </arguments>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <groupId>org.codehaus.mojo</groupId>
+ <artifactId>build-helper-maven-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>attach-camel-exe</id>
+ <phase>package</phase>
+ <goals>
+ <goal>attach-artifact</goal>
+ </goals>
+ <configuration>
+ <artifacts>
+ <artifact>
+
<file>${project.build.directory}/camel.exe</file>
+ <type>exe</type>
+ </artifact>
+ </artifacts>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+ </profile>
+
+ <!--
+ Release gate: fails the build before packaging if the native
camel.exe was not
+ produced. Enabled only when -Dcamel.exe.requireWindowsExe=true
(release job on
+ a Windows x64 host), so ordinary cross-platform dev builds are
unaffected.
+ -->
+ <profile>
+ <id>require-windows-exe</id>
+ <activation>
+ <property>
+ <name>camel.exe.requireWindowsExe</name>
+ <value>true</value>
+ </property>
+ </activation>
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-enforcer-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>require-camel-exe</id>
+ <phase>prepare-package</phase>
+ <goals>
+ <goal>enforce</goal>
+ </goals>
+ <configuration>
+ <rules>
+ <requireFilesExist>
+ <files>
+
<file>${project.build.directory}/camel.exe</file>
+ </files>
+ </requireFilesExist>
+ </rules>
+ <fail>true</fail>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+ </profile>
+ </profiles>
+</project>
diff --git a/tooling/camel-exe/src/main/native/README.md
b/tooling/camel-exe/src/main/native/README.md
new file mode 100644
index 000000000000..506b2bde8c71
--- /dev/null
+++ b/tooling/camel-exe/src/main/native/README.md
@@ -0,0 +1,26 @@
+# Native build (`camel.c`)
+
+Compiler flags, MSVC setup, and release-gate details for the `camel.exe`
bootstrap.
+See the [module README](../../../README.md) for purpose, architecture, and
integration
+with `camel-launcher`.
+
+## MSVC setup
+
+The `build-windows-exe` Maven profile activates automatically on Windows and
invokes
+`cl.exe`. MSVC must be on PATH — open a developer command prompt (e.g. run
+`vcvars64.bat`) or provision MSVC in CI before running Maven.
+
+## Compiler invocation
+
+ cl /nologo /W4 /O1 /MT /Brepro /Fe:target\camel.exe
src\main\native\camel.c /link /Brepro /SUBSYSTEM:CONSOLE
+
+- `/MT` — static CRT (no runtime DLL dependency)
+- `/Brepro` — deterministic PE header timestamp on both compiler and linker, so
+ repeated builds from the same source produce byte-identical output
+
+## Release gate
+
+Release builds pass `-Dcamel.exe.requireWindowsExe=true`, which activates the
+`require-windows-exe` profile. The enforcer fails the build if
`target/camel.exe` is
+absent. Because MSVC cannot cross-compile from macOS/Linux, the launcher
ZIP/TAR that
+carries `camel.exe` must be assembled on a Windows x64 host.
diff --git a/tooling/camel-exe/src/main/native/camel.c
b/tooling/camel-exe/src/main/native/camel.c
new file mode 100644
index 000000000000..8f77121d1046
--- /dev/null
+++ b/tooling/camel-exe/src/main/native/camel.c
@@ -0,0 +1,146 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * camel.exe - minimal WinGet-compatible bootstrap for the Camel CLI launcher.
+ *
+ * It locates the camel.bat sitting in the same directory, forwards the
caller's
+ * exact command-line tail (preserving Unicode and Windows quoting), inherits
the
+ * standard streams, and returns the child process exit code. It performs NO
Java
+ * discovery and NO Camel option parsing; that all lives in camel.bat.
+ */
+
+#include <windows.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <wchar.h>
+
+/* Return the command-line tail after argv[0], preserving the caller's
quoting. */
+static LPWSTR skip_argv0(LPWSTR cmd) {
+ LPWSTR p = cmd;
+ if (*p == L'"') {
+ p++;
+ while (*p && *p != L'"') {
+ p++;
+ }
+ if (*p == L'"') {
+ p++;
+ }
+ } else {
+ while (*p && *p != L' ' && *p != L'\t') {
+ p++;
+ }
+ }
+ while (*p == L' ' || *p == L'\t') {
+ p++;
+ }
+ return p;
+}
+
+/*
+ * Return a heap-allocated wide string holding the directory that contains this
+ * exe, or NULL on failure. The caller must free() the result. Uses a doubling
+ * loop so paths beyond MAX_PATH (260) work on Windows 10+ with long-path
+ * support enabled.
+ */
+static wchar_t *get_exe_dir(void) {
+ DWORD bufSize = MAX_PATH;
+ wchar_t *buf = (wchar_t *) malloc(bufSize * sizeof(wchar_t));
+ if (buf == NULL) {
+ return NULL;
+ }
+ for (;;) {
+ DWORD n = GetModuleFileNameW(NULL, buf, bufSize);
+ if (n == 0) {
+ free(buf);
+ return NULL;
+ }
+ if (n < bufSize) {
+ break;
+ }
+ bufSize *= 2;
+ wchar_t *tmp = (wchar_t *) realloc(buf, bufSize * sizeof(wchar_t));
+ if (tmp == NULL) {
+ free(buf);
+ return NULL;
+ }
+ buf = tmp;
+ }
+ wchar_t *slash = wcsrchr(buf, L'\\');
+ if (slash == NULL) {
+ free(buf);
+ return NULL;
+ }
+ *slash = L'\0';
+ return buf;
+}
+
+int wmain(void) {
+ wchar_t *exePath = get_exe_dir();
+ if (exePath == NULL) {
+ fwprintf(stderr, L"camel: cannot resolve launcher directory\n");
+ return 1;
+ }
+
+ LPWSTR tail = skip_argv0(GetCommandLineW());
+
+ /*
+ * cmd.exe /S /C ""<dir>\camel.bat" <tail>"
+ * With /S and a command wrapped in an outer pair of quotes, cmd strips the
+ * first and last quote and executes the remainder verbatim, so the inner
+ * quotes around the (possibly spaced/Unicode) camel.bat path survive.
+ */
+ size_t cap = wcslen(exePath) + wcslen(tail) + 64;
+ LPWSTR cmdline = (LPWSTR) malloc(cap * sizeof(wchar_t));
+ if (cmdline == NULL) {
+ fwprintf(stderr, L"camel: out of memory\n");
+ free(exePath);
+ return 1;
+ }
+ int ret = _snwprintf_s(cmdline, cap, _TRUNCATE,
+ L"cmd.exe /S /C \"\"%s\\camel.bat\" %s\"", exePath,
tail);
+ if (ret == -1) {
+ fwprintf(stderr, L"camel: command line was truncated\n");
+ free(cmdline);
+ free(exePath);
+ return 1;
+ }
+
+ STARTUPINFOW si;
+ PROCESS_INFORMATION pi;
+ ZeroMemory(&si, sizeof(si));
+ si.cb = sizeof(si);
+ ZeroMemory(&pi, sizeof(pi));
+
+ /* bInheritHandles=TRUE and no STARTF_USESTDHANDLES: child shares our
console. */
+ if (!CreateProcessW(NULL, cmdline, NULL, NULL, TRUE, 0, NULL, NULL, &si,
&pi)) {
+ fwprintf(stderr, L"camel: failed to start camel.bat (error %lu)\n",
GetLastError());
+ free(cmdline);
+ free(exePath);
+ return 1;
+ }
+
+ WaitForSingleObject(pi.hProcess, INFINITE);
+ DWORD code = 1;
+ if (!GetExitCodeProcess(pi.hProcess, &code)) {
+ fwprintf(stderr, L"camel: failed to get exit code (error %lu)\n",
GetLastError());
+ }
+ CloseHandle(pi.hProcess);
+ CloseHandle(pi.hThread);
+ free(cmdline);
+ free(exePath);
+ return (int) code;
+}
diff --git
a/tooling/camel-exe/src/test/java/org/apache/camel/tooling/exe/CamelExeBootstrapTest.java
b/tooling/camel-exe/src/test/java/org/apache/camel/tooling/exe/CamelExeBootstrapTest.java
new file mode 100644
index 000000000000..3171012393c3
--- /dev/null
+++
b/tooling/camel-exe/src/test/java/org/apache/camel/tooling/exe/CamelExeBootstrapTest.java
@@ -0,0 +1,217 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.tooling.exe;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.condition.EnabledOnOs;
+import org.junit.jupiter.api.condition.OS;
+import org.junit.jupiter.api.io.TempDir;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+/**
+ * Windows-only behavioral test for the native camel.exe bootstrap. Requires
the build-windows-exe profile to have
+ * produced target/camel.exe; the test fails loudly if it is missing so a
broken native build does not pass silently.
+ */
+@EnabledOnOs(OS.WINDOWS)
+class CamelExeBootstrapTest {
+
+ private static final Path BUILT_EXE = Paths.get("target/camel.exe");
+ private static final String CAPTURED_ARGS_FILE = "camel-args.txt";
+ private static final String CAPTURE_ARGS_SCRIPT = "capture-arg.ps1";
+
+ /** Writes a fake camel.bat that echoes each argument on its own line and
returns exitCode. */
+ private Path fakeBat(Path dir, int exitCode) throws Exception {
+ Path bat = dir.resolve("camel.bat");
+ String body = "@echo off\r\n"
+ + ":loop\r\n"
+ + "if \"%~1\"==\"\" goto done\r\n"
+ + "echo ARG=%~1\r\n"
+ + "shift\r\n"
+ + "goto loop\r\n"
+ + ":done\r\n"
+ + "exit /b " + exitCode + "\r\n";
+ Files.writeString(bat, body, StandardCharsets.UTF_8);
+ return bat;
+ }
+
+ /**
+ * Writes a fake camel.bat that appends each forwarded argument to {@link
#CAPTURED_ARGS_FILE} as UTF-8.
+ * <p>
+ * Non-ASCII argument checks cannot rely on parsing process stdout:
cmd.exe {@code echo} uses the console OEM code
+ * page on Windows CI hosts, so Unicode forwarded correctly by camel.exe
is still mangled (for example {@code ü}
+ * becomes {@code ?}) before Java reads the pipe. We therefore capture
arguments in a UTF-8 file via a helper
+ * PowerShell script. The batch file passes all arguments at once using
{@code %*} (the raw command-line tail)
+ * rather than iterating with {@code %~1}, because per-argument expansion
through cmd.exe's batch variable
+ * substitution can corrupt non-ASCII characters on certain OEM code pages.
+ */
+ private void fakeBatCapturingArgsToUtf8File(Path dir, int exitCode) throws
Exception {
+ Path captureScript = dir.resolve(CAPTURE_ARGS_SCRIPT);
+ String ps1 = "$outFile = $args[0]\r\n"
+ + "for ($i = 1; $i -lt $args.Count; $i++) {\r\n"
+ + " [System.IO.File]::AppendAllText($outFile,\r\n"
+ + " $args[$i] + [Environment]::NewLine,\r\n"
+ + " [System.Text.UTF8Encoding]::new($false))\r\n"
+ + "}\r\n"
+ + "exit " + exitCode + "\r\n";
+ Files.writeString(captureScript, ps1, StandardCharsets.UTF_8);
+
+ Path bat = dir.resolve("camel.bat");
+ String body = "@powershell -NoProfile -ExecutionPolicy Bypass -File
\"%~dp0" + CAPTURE_ARGS_SCRIPT + "\" "
+ + "\"%~dp0" + CAPTURED_ARGS_FILE + "\" %*\r\n"
+ + "@exit /b %ERRORLEVEL%\r\n";
+ Files.writeString(bat, body, StandardCharsets.UTF_8);
+ }
+
+ private List<String> readCapturedArgs(Path dir) throws Exception {
+ Path file = dir.resolve(CAPTURED_ARGS_FILE);
+ if (!Files.exists(file)) {
+ return List.of();
+ }
+ return Files.readAllLines(file, StandardCharsets.UTF_8);
+ }
+
+ private Path stagedExe(Path dir) throws Exception {
+ assertTrue(Files.exists(BUILT_EXE),
+ "target/camel.exe must be built by the build-windows-exe
profile before this test");
+ Path exe = dir.resolve("camel.exe");
+ Files.copy(BUILT_EXE, exe);
+ return exe;
+ }
+
+ private static final class Result {
+ int exit;
+ String stdout;
+ }
+
+ private Result run(Path exe, String... args) throws Exception {
+ List<String> cmd = new ArrayList<>();
+ cmd.add(exe.toString());
+ for (String a : args) {
+ cmd.add(a);
+ }
+ ProcessBuilder pb = new ProcessBuilder(cmd);
+ pb.redirectErrorStream(true);
+ Process p = pb.start();
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ // Drain stdout on a background thread: reading it inline would block
until the pipe closes
+ // at process exit, making the waitFor timeout below unreachable if
camel.exe hangs.
+ Thread drain = new Thread(() -> {
+ try {
+ p.getInputStream().transferTo(out);
+ } catch (IOException e) {
+ // stream closes abruptly when the process is destroyed on
timeout below
+ }
+ });
+ drain.start();
+ try {
+ boolean exited = p.waitFor(60, TimeUnit.SECONDS);
+ if (!exited) {
+ p.destroyForcibly();
+ }
+ assertTrue(exited, "camel.exe did not exit in time");
+ drain.join(TimeUnit.SECONDS.toMillis(5));
+ Result r = new Result();
+ r.exit = p.exitValue();
+ r.stdout = out.toString(StandardCharsets.UTF_8);
+ return r;
+ } finally {
+ if (p.isAlive()) {
+ p.destroyForcibly();
+ }
+ }
+ }
+
+ @Test
+ void forwardsArgumentsToAdjacentCamelBat(@TempDir Path dir) throws
Exception {
+ fakeBat(dir, 0);
+ Path exe = stagedExe(dir);
+
+ Result r = run(exe, "version");
+
+ assertEquals(0, r.exit);
+ assertTrue(r.stdout.contains("ARG=version"), r.stdout);
+ }
+
+ @Test
+ void preservesArgumentWithSpaces(@TempDir Path dir) throws Exception {
+ fakeBat(dir, 0);
+ Path exe = stagedExe(dir);
+
+ Result r = run(exe, "run", "my route.yaml");
+
+ assertTrue(r.stdout.contains("ARG=run"), r.stdout);
+ assertTrue(r.stdout.contains("ARG=my route.yaml"), "spaced argument
must survive as one token: " + r.stdout);
+ }
+
+ @Test
+ void preservesUnicodeArgument(@TempDir Path dir) throws Exception {
+ fakeBatCapturingArgsToUtf8File(dir, 0);
+ Path exe = stagedExe(dir);
+
+ Result r = run(exe, "run", "rüte-über.yaml");
+
+ assertEquals(0, r.exit);
+ List<String> captured = readCapturedArgs(dir);
+ assertTrue(captured.contains("run"), captured.toString());
+ assertTrue(captured.contains("rüte-über.yaml"), "Unicode argument must
survive: " + captured);
+ }
+
+ @Test
+ void propagatesChildExitCode(@TempDir Path dir) throws Exception {
+ fakeBat(dir, 42);
+ Path exe = stagedExe(dir);
+
+ Result r = run(exe, "version");
+
+ assertEquals(42, r.exit, "camel.exe must return camel.bat's exit
code");
+ }
+
+ @Test
+ void worksWhenExeDirectoryHasSpaces(@TempDir Path base) throws Exception {
+ Path dir = base.resolve("Program Files X");
+ Files.createDirectories(dir);
+ fakeBat(dir, 0);
+ Path exe = stagedExe(dir);
+
+ Result r = run(exe, "version");
+
+ assertEquals(0, r.exit, r.stdout);
+ assertTrue(r.stdout.contains("ARG=version"), r.stdout);
+ }
+
+ @Test
+ void failsGracefullyWhenCamelBatIsMissing(@TempDir Path dir) throws
Exception {
+ Path exe = stagedExe(dir);
+
+ Result r = run(exe, "version");
+
+ assertTrue(r.exit != 0, "exit code must be non-zero when camel.bat is
missing");
+ assertTrue(r.stdout.length() > 0, "error message must be present in
output");
+ }
+}
diff --git
a/tooling/maven/camel-repackager-maven-plugin/src/main/java/org/apache/camel/maven/RepackageMojo.java
b/tooling/maven/camel-repackager-maven-plugin/src/main/java/org/apache/camel/maven/RepackageMojo.java
index 3ffeec345ba4..3c2bab83feac 100644
---
a/tooling/maven/camel-repackager-maven-plugin/src/main/java/org/apache/camel/maven/RepackageMojo.java
+++
b/tooling/maven/camel-repackager-maven-plugin/src/main/java/org/apache/camel/maven/RepackageMojo.java
@@ -129,7 +129,13 @@ public class RepackageMojo extends AbstractMojo {
}
}
- private boolean includeArtifact(Artifact artifact) {
+ boolean includeArtifact(Artifact artifact) {
+ // Only jar-type artifacts are valid Spring Boot loader libraries.
Non-jar artifacts
+ // (e.g. the native camel-exe:exe bootstrap, which camel-launcher
depends on purely to
+ // stage bin/camel.exe via the assembly descriptor) must never be
embedded in BOOT-INF/lib.
+ if (!"jar".equals(artifact.getType())) {
+ return false;
+ }
String scope = artifact.getScope();
// Include compile and runtime dependencies
return Artifact.SCOPE_COMPILE.equals(scope) ||
diff --git
a/tooling/maven/camel-repackager-maven-plugin/src/test/java/org/apache/camel/maven/RepackageMojoTest.java
b/tooling/maven/camel-repackager-maven-plugin/src/test/java/org/apache/camel/maven/RepackageMojoTest.java
index 279a8aeab423..329a43d57197 100644
---
a/tooling/maven/camel-repackager-maven-plugin/src/test/java/org/apache/camel/maven/RepackageMojoTest.java
+++
b/tooling/maven/camel-repackager-maven-plugin/src/test/java/org/apache/camel/maven/RepackageMojoTest.java
@@ -18,6 +18,9 @@ package org.apache.camel.maven;
import java.io.File;
+import org.apache.maven.artifact.Artifact;
+import org.apache.maven.artifact.DefaultArtifact;
+import org.apache.maven.artifact.handler.DefaultArtifactHandler;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;
@@ -61,4 +64,46 @@ public class RepackageMojoTest {
assertTrue(true, "Placeholder test - would verify dependency
inclusion");
}
+
+ @Test
+ public void testCompileScopedJarIsIncluded() {
+ RepackageMojo mojo = new RepackageMojo();
+ Artifact artifact = artifact("org.apache.camel", "camel-jbang-core",
"jar", Artifact.SCOPE_COMPILE);
+
+ assertTrue(mojo.includeArtifact(artifact),
+ "a compile-scoped jar dependency must be bundled into
BOOT-INF/lib");
+ }
+
+ @Test
+ public void testNonJarArtifactIsExcludedEvenWhenCompileScoped() {
+ // camel-launcher depends on camel-exe:exe purely so the assembly
descriptor can stage
+ // bin/camel.exe; it must never end up embedded as a Spring Boot
loader library.
+ RepackageMojo mojo = new RepackageMojo();
+ Artifact artifact = artifact("org.apache.camel", "camel-exe", "exe",
Artifact.SCOPE_COMPILE);
+
+ assertFalse(mojo.includeArtifact(artifact),
+ "a non-jar artifact (e.g. the native camel-exe:exe bootstrap)
must not be bundled into BOOT-INF/lib");
+ }
+
+ @Test
+ public void testProvidedCamelJarIsIncluded() {
+ RepackageMojo mojo = new RepackageMojo();
+ Artifact artifact = artifact("org.apache.camel", "camel-util", "jar",
Artifact.SCOPE_PROVIDED);
+
+ assertTrue(mojo.includeArtifact(artifact),
+ "a provided-scope org.apache.camel jar dependency must still
be bundled");
+ }
+
+ @Test
+ public void testProvidedNonCamelJarIsExcluded() {
+ RepackageMojo mojo = new RepackageMojo();
+ Artifact artifact = artifact("org.jolokia", "jolokia-agent-jvm",
"jar", Artifact.SCOPE_PROVIDED);
+
+ assertFalse(mojo.includeArtifact(artifact),
+ "a provided-scope non-Camel jar dependency must not be
bundled");
+ }
+
+ private static Artifact artifact(String groupId, String artifactId, String
type, String scope) {
+ return new DefaultArtifact(groupId, artifactId, "1.0", scope, type,
null, new DefaultArtifactHandler(type));
+ }
}
diff --git a/tooling/pom.xml b/tooling/pom.xml
index 16fbb6fe64e4..282288030da4 100644
--- a/tooling/pom.xml
+++ b/tooling/pom.xml
@@ -42,6 +42,7 @@
<modules>
<module>parent</module>
+ <module>camel-exe</module>
<module>spi-annotations</module>
<module>camel-tooling-model</module>
<module>camel-util-json</module>