Skip to content

Commit

Permalink
Merge pull request #177 from ecmwf-actions/ecbundle
Browse files Browse the repository at this point in the history
Add ecbundle support
  • Loading branch information
figi44 authored Nov 9, 2023
2 parents ea19bd5 + a791ecd commit a7b0093
Show file tree
Hide file tree
Showing 7 changed files with 293 additions and 81 deletions.
4 changes: 4 additions & 0 deletions action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@ inputs:
description: Whether to use CMake for build configuration, instead of ecbuild.
required: true
default: "false"
ecbundle:
description: Whether to use ecbundle for building bundles.
required: true
default: "false"
cmake_options:
description:
"The list of ecbuild/CMake options to be passed during the current repository build configuration
Expand Down
2 changes: 1 addition & 1 deletion dist/index.js

Large diffs are not rendered by default.

213 changes: 169 additions & 44 deletions src/build-package.ts
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@ const buildPackage = async (
sourceDir: string,
installDir: string,
cmake: boolean,
ecbundle: boolean,
cmakeOptions: string | null,
ctestOptions: string | null,
test: boolean,
Expand All @@ -134,6 +135,7 @@ const buildPackage = async (
compiler: string,
env: EnvironmentVariables,
parallelismFactor: string,
token: string,
cpackGenerator?: string,
cpackOptions?: string,
toolchainFile?: string
Expand All @@ -146,6 +148,28 @@ const buildPackage = async (
let configurePath;
let configureOptions = [];

if (repo === "ecbundle") {
const ecbundlePath = path.join(path.resolve(sourceDir));
await extendPaths(env, ecbundlePath, repo);
core.endGroup();
return true;
}

if (ecbundle) {
return await ecbundleBuild(
test,
token,
parallelismFactor,
env,
cmakeOptions,
ctestOptions,
installDir,
path.resolve(sourceDir),
cpackGenerator,
cpackOptions
);
}

if (cmake) {
configurePath = "cmake";
configureOptions.push(`-DCMAKE_INSTALL_PREFIX=${installDir}`);
Expand Down Expand Up @@ -309,13 +333,15 @@ const buildPackage = async (
[configurePath, ...configureOptions, srcDir],
options
);

if (isError(exitCode, "Error configuring package")) return false;

exitCode = await exec.exec("env", ["cmake", "--build", "."], options);

if (isError(exitCode, "Error building package")) return false;

exitCode = await exec.exec("env", ["cmake", "--install", "."], options);
if (isError(exitCode, "Error installing package")) return false;
await extendPaths(env, installDir, repo);

if (test) {
exitCode = await exec.exec(
"env",
Expand Down Expand Up @@ -398,51 +424,17 @@ const buildPackage = async (
}
}

exitCode = await exec.exec("env", ["cmake", "--install", "."], options);

if (isError(exitCode, "Error installing package")) return false;

await extendPaths(env, installDir, repo);

if (cpackGenerator) {
const cpackOptionsParsed = [];
if (cpackOptions) {
cpackOptionsParsed.push(...parseOptions(cpackOptions));
core.info(`==> cpackOptionsParsed: ${cpackOptionsParsed}`);
}

exitCode = await exec.exec(
"env",
[
"cpack",
"-G",
cpackGenerator.toUpperCase(),
...cpackOptionsParsed,
],
options
const cpackSuccess = await cpack(
options,
sourceDir,
buildDir,
env,
cpackGenerator,
cpackOptions
);
if (isError(exitCode, "Error while generating package"))
return false;

const packageVersion = getProjectVersion(sourceDir);
if (isError(!packageVersion, "Error reading version number")) {
return false;
}

const packageFileNames = fs
.readdirSync(buildDir)
.filter(
(file) =>
path.extname(file) ===
`.${cpackGenerator.toLowerCase()}`
);

if (packageFileNames.length == 1) {
env.PACKAGE_PATH = path.join(buildDir, packageFileNames[0]);
} else {
isError(true, "Generated binary not found");
if (isError(!cpackSuccess, "Error generating package."))
return false;
}
}
} catch (error) {
if (error instanceof Error) isError(true, error.message);
Expand All @@ -454,4 +446,137 @@ const buildPackage = async (
return true;
};

const ecbundleBuild = async (
test: boolean,
token: string,
parallelismFactor: string,
env: EnvironmentVariables,
cmakeOptions: string | null,
ctestOptions: string | null,
installDir: string,
sourceDir: string,
cpackGenerator?: string,
cpackOptions?: string
): Promise<boolean> => {
const options = {
cwd: sourceDir,
env: {
CMAKE_BUILD_PARALLEL_LEVEL: parallelismFactor,
...(test ? { CTEST_OUTPUT_ON_FAILURE: "1" } : {}), // show output of failing tests only
...(test ? { CTEST_PARALLEL_LEVEL: parallelismFactor } : {}),
...process.env, // preserve existing environment
...env, // compiler env must win
},
};

// ecbundle create
let exitCode = await exec.exec(
"env",
[
"ecbundle",
"create",
`--github-token=${token}`,
"--shallow",
`--threads=${parallelismFactor}`,
],
options
);
if (isError(exitCode, "Error creating bundle")) return false;

// prepare cmake options
let configureOptions = [];
if (cmakeOptions) {
configureOptions.push(...parseOptions(cmakeOptions));
}
configureOptions = expandShellVariables({ configureOptions }, options.env);

// ecbundle build and install
exitCode = await exec.exec(
"env",
[
"ecbundle",
"build",
`--install`,
`--threads=${parallelismFactor}`,
`--install-dir=${installDir}`,
...(configureOptions.length
? [`--cmake="${configureOptions.join(" ")}"`]
: []),
],
options
);
if (isError(exitCode, "Error building bundle")) return false;

// ctest
if (test) {
let testOptions = [];

if (ctestOptions) {
testOptions.push(...parseOptions(ctestOptions));
core.info(`==> testOptions: ${testOptions}`);
}
testOptions = expandShellVariables({ testOptions }, options.env);
options.cwd = path.join(sourceDir, "build");
exitCode = await exec.exec("env", ["ctest", ...testOptions], options);

if (isError(exitCode, "Error testing bundle")) return false;
}

if (cpackGenerator) {
const cpackSuccess = await cpack(
options,
sourceDir,
path.join(sourceDir, "build"),
env,
cpackGenerator,
cpackOptions
);
if (isError(!cpackSuccess, "Error generating package.")) return false;
}

core.endGroup();
return true;
};

const cpack = async (
options: exec.ExecOptions,
sourceDir: string,
buildDir: string,
env: EnvironmentVariables,
cpackGenerator: string,
cpackOptions?: string
) => {
const cpackOptionsParsed = [];
if (cpackOptions) {
cpackOptionsParsed.push(...parseOptions(cpackOptions));
core.info(`==> cpackOptionsParsed: ${cpackOptionsParsed}`);
}
options.cwd = buildDir;
const exitCode = await exec.exec(
"env",
["cpack", "-G", cpackGenerator.toUpperCase(), ...cpackOptionsParsed],
options
);
if (isError(exitCode, "Error while generating package")) return false;

const packageVersion = getProjectVersion(sourceDir);
if (isError(!packageVersion, "Error reading version number")) {
return false;
}

const packageFileNames = fs
.readdirSync(buildDir)
.filter(
(file) => path.extname(file) === `.${cpackGenerator.toLowerCase()}`
);

if (packageFileNames.length == 1) {
env.PACKAGE_PATH = path.join(buildDir, packageFileNames[0]);
} else {
isError(true, "Generated binary not found");
return false;
}
return true;
};

export default buildPackage;
1 change: 1 addition & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import main from "./main";
* standard Github `owner/name@ref` format. `@ref` is optional, takes precedence over `sha` input.
* @param {string} sha The currently checked out source repository commit SHA.
* @param {boolean} cmake Whether to use CMake for build configuration, instead of ecbuild.
* @param {boolean} ecbundle Whether to use ecbundle for building bundles.
* @param {string} cmake_options The list of ecbuild/CMake options to be passed during the current
* repository build configuration phase. Use the form of `-DCMAKE_VAR=1 -DCMAKE_ANOTHER_VAR=0` to define multiple
* options. If left empty, the repository will be configured with default options only.
Expand Down
11 changes: 8 additions & 3 deletions src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ const main = async () => {
const repositoryInput = core.getInput("repository", { required: true });
const shaInput = core.getInput("sha", { required: true });
const cmake = core.getBooleanInput("cmake", { required: true });
const ecbundle = core.getBooleanInput("ecbundle", { required: true });
const cmakeOptions = core.getInput("cmake_options", {
required: false,
});
Expand Down Expand Up @@ -129,7 +130,7 @@ const main = async () => {
dependencyBranchSpecific || dependencyBranchDefault;

// If the build is not forced, first try to download an artifact.
if (!forceBuild) {
if (!forceBuild && repo !== "ecbundle") {
const isArtifactDownloaded = await downloadArtifact(
dependencyRepository,
dependencyBranch,
Expand All @@ -149,7 +150,7 @@ const main = async () => {

// Check if we already cached the build of this package.
// Skip this part if we were told to always recreate cache.
if (!recreateCache) {
if (!recreateCache && repo !== "ecbundle") {
const cacheHit = await restoreCache(
dependencyRepository,
dependencyBranch,
Expand Down Expand Up @@ -184,6 +185,7 @@ const main = async () => {
path.join(downloadDir, repo),
path.join(installDir, repo),
cmake,
ecbundle,
dependencyCmakeOptions,
null,
false,
Expand All @@ -192,14 +194,15 @@ const main = async () => {
compiler,
env,
parallelismFactor,
githubToken,
undefined,
undefined,
toolchain_file
);

if (!isBuilt) return Promise.reject("Error building dependency");

if (saveCacheInput) {
if (saveCacheInput && repo !== "ecbundle") {
// Save built package to the cache.
await saveCache(
dependencyRepository,
Expand Down Expand Up @@ -251,6 +254,7 @@ const main = async () => {
workspace,
path.join(installDir, repo),
cmake,
ecbundle,
cmakeOptions,
ctestOptions,
selfTest,
Expand All @@ -259,6 +263,7 @@ const main = async () => {
compiler,
env,
parallelismFactor,
githubToken,
cpackGenerator,
cpackOptions,
toolchain_file
Expand Down
Loading

0 comments on commit a7b0093

Please sign in to comment.