Skip to content

Commit ec23251

Browse files
committed
For Podman, invoke host cpp when available and fail clearly otherwise,
1 parent 65f98a5 commit ec23251

8 files changed

Lines changed: 76 additions & 3 deletions

File tree

src/spec-node/dockerCompose.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import { getExtendImageBuildInfo, updateRemoteUserUID } from './containerFeature
1818
import { Mount, parseMount } from '../spec-configuration/containerFeaturesConfiguration';
1919
import path from 'path';
2020
import { getDevcontainerMetadata, getImageBuildInfoFromDockerfile, getImageBuildInfoFromImage, getImageMetadataFromContainer, ImageBuildInfo, lifecycleCommandOriginMapFromMetadata, mergeConfiguration, MergedDevContainerConfig } from './imageMetadata';
21-
import { ensureDockerfileHasFinalStageName } from './dockerfileUtils';
21+
import { ensureDockerfileHasFinalStageName, preprocessDockerfileIn } from './dockerfileUtils';
2222
import { randomUUID } from 'crypto';
2323

2424
const projectLabel = 'com.docker.compose.project';
@@ -167,7 +167,10 @@ export async function buildAndExtendDockerCompose(configWithRaw: SubstitutedConf
167167
if (serviceInfo.build) {
168168
const { context, dockerfilePath, target } = serviceInfo.build;
169169
const resolvedDockerfilePath = cliHost.path.isAbsolute(dockerfilePath) ? dockerfilePath : path.resolve(context, dockerfilePath);
170-
const originalDockerfile = (await cliHost.readFile(resolvedDockerfilePath)).toString();
170+
let originalDockerfile = (await cliHost.readFile(resolvedDockerfilePath)).toString();
171+
if (params.isPodman && resolvedDockerfilePath.endsWith('.in')) {
172+
originalDockerfile = await preprocessDockerfileIn(resolvedDockerfilePath, cliHost.exec, output);
173+
}
171174
dockerfile = originalDockerfile;
172175
if (target) {
173176
// Explictly set build target for the dev container build features on that

src/spec-node/dockerfileUtils.ts

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
*--------------------------------------------------------------------------------------------*/
55

66
import * as semver from 'semver';
7+
import { ExecFunction, runCommandNoPty } from '../spec-common/commonUtils';
8+
import { Log } from '../spec-utils/log';
79
import { Mount } from '../spec-configuration/containerFeaturesConfiguration';
810

911

@@ -260,6 +262,37 @@ export function ensureDockerfileHasFinalStageName(dockerfile: string, defaultLas
260262
return { lastStageName: defaultLastStageName, modifiedDockerfile: modifiedDockerfile };
261263
}
262264

265+
/**
266+
* Preprocess a Dockerfile.in file using the host cpp tool.
267+
* This is needed when using Podman, which supports cpp preprocessing of .in files natively.
268+
* Throws a clear error if cpp is not available on the host.
269+
*/
270+
export async function preprocessDockerfileIn(dockerfilePath: string, exec: ExecFunction, output: Log): Promise<string> {
271+
let result: { stdout: Buffer; stderr: Buffer };
272+
try {
273+
result = await runCommandNoPty({
274+
exec,
275+
cmd: 'cpp',
276+
// -undef: do not predefine platform/compiler macros
277+
// -fdirectives-only: only process directives, do not expand macros
278+
// -w: suppress warnings
279+
// -P: suppress linemarker output lines
280+
args: ['-undef', '-fdirectives-only', '-w', '-P', dockerfilePath],
281+
output,
282+
});
283+
} catch (err: any) {
284+
if (err?.code === 'ENOENT' || err?.message?.includes('ENOENT')) {
285+
throw new Error(
286+
`Preprocessing '${dockerfilePath}' requires 'cpp', but it was not found on the host. ` +
287+
`Please install cpp (e.g. "sudo apt-get install cpp") to use Dockerfile.in files with Podman.`
288+
);
289+
}
290+
const stderrText = err?.stderr ? `\n${(err.stderr as Buffer).toString()}` : '';
291+
throw new Error(`Failed to preprocess '${dockerfilePath}' using cpp: ${err?.message || String(err)}${stderrText}`);
292+
}
293+
return result.stdout.toString();
294+
}
295+
263296
export function supportsBuildContexts(dockerfile: Dockerfile) {
264297
const version = dockerfile.preamble.version;
265298
if (!version) {

src/spec-node/singleContainer.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import { DevContainerConfig, DevContainerFromDockerfileConfig, DevContainerFromI
1212
import { LogLevel, Log, makeLog } from '../spec-utils/log';
1313
import { extendImage, getExtendImageBuildInfo, updateRemoteUserUID } from './containerFeatures';
1414
import { getDevcontainerMetadata, getImageBuildInfoFromDockerfile, getImageMetadataFromContainer, ImageMetadataEntry, lifecycleCommandOriginMapFromMetadata, mergeConfiguration, MergedDevContainerConfig } from './imageMetadata';
15-
import { ensureDockerfileHasFinalStageName, generateMountCommand } from './dockerfileUtils';
15+
import { ensureDockerfileHasFinalStageName, generateMountCommand, preprocessDockerfileIn } from './dockerfileUtils';
1616

1717
export const hostFolderLabel = 'devcontainer.local_folder'; // used to label containers created from a workspace/folder
1818
export const configFileLabel = 'devcontainer.config_file';
@@ -131,6 +131,9 @@ async function buildAndExtendImage(buildParams: DockerResolverParameters, config
131131
}
132132

133133
let dockerfile = (await cliHost.readFile(dockerfilePath)).toString();
134+
if (buildParams.isPodman && dockerfilePath.endsWith('.in')) {
135+
dockerfile = await preprocessDockerfileIn(dockerfilePath, cliHost.exec, output);
136+
}
134137
const originalDockerfile = dockerfile;
135138
let baseName = 'dev_container_auto_added_stage_label';
136139
if (config.build?.target) {
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
{
2+
"build": {
3+
"dockerfile": "Dockerfile.in"
4+
},
5+
"features": {
6+
"ghcr.io/devcontainers/features/github-cli:1": {
7+
"version": "latest"
8+
}
9+
}
10+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
#define BASE_IMAGE ubuntu:20.04
2+
#define INSTALL_NODE
3+
#define INSTALL_PYTHON
4+
5+
FROM BASE_IMAGE
6+
7+
#ifdef INSTALL_NODE
8+
RUN apt-get update && apt-get install -y nodejs
9+
#endif
10+
11+
#ifdef INSTALL_PYTHON
12+
RUN apt-get update && apt-get install -y python3
13+
#endif
14+
15+
#include "common.Dockerfile"
16+
#include "tools.Dockerfile"
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
RUN apt-get update && apt-get install -y curl wget
2+
3+
ENV APP_ENV=development
4+
ENV APP_DEBUG=true
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
#!/bin/sh
2+
echo "hello! podman with cpp test"
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
RUN apt-get update && apt-get install -y vim
2+
COPY ./test.sh /usr/local/bin/test.sh

0 commit comments

Comments
 (0)