diff --git a/.github/workflows/workflow.yml b/.github/workflows/workflow.yml index 89b47ada..162c1dfe 100644 --- a/.github/workflows/workflow.yml +++ b/.github/workflows/workflow.yml @@ -53,6 +53,8 @@ jobs: - windows-latest ocaml-compiler: - "5.2" + windows-environment: + - "" allow-prerelease-opam: - false include: @@ -62,6 +64,10 @@ jobs: - os: ubuntu-latest ocaml-compiler: "5.2" allow-prerelease-opam: true + - os: windows-latest + ocaml-compiler: "5.2" + windows-environment: msys2 + allow-prerelease-opam: false runs-on: ${{ matrix.os }} @@ -74,5 +80,6 @@ jobs: with: ocaml-compiler: ${{ matrix.ocaml-compiler }} allow-prerelease-opam: ${{ matrix.allow-prerelease-opam }} + windows-environment: ${{ matrix.windows-environment }} - run: opam install ssl diff --git a/action.yml b/action.yml index e0ba3271..db00a1b1 100644 --- a/action.yml +++ b/action.yml @@ -37,6 +37,9 @@ inputs: description: The prefix of the cache keys. required: false default: v1 + windows-environment: + description: Whether to use `cygwin` (default) or `msys2` for windows installs. + required: false github-token: description: DO NOT SET THIS. required: false diff --git a/dist/index.js b/dist/index.js index b2b7e958..7a50acb3 100644 --- a/dist/index.js +++ b/dist/index.js @@ -147223,6 +147223,19 @@ const ALLOW_PRERELEASE_OPAM = lib_core.getBooleanInput("allow-prerelease-opam", required: false, trimWhitespace: true, }); +const WINDOWS_ENVIRONMENT = (() => { + const input = lib_core.getInput("windows-environment"); + if (!(PLATFORM === "windows" || input === "")) { + lib_core.error("windows-environment is only supported on windows"); + } + if (input === "msys2" || input === "cygwin") { + return input; + } + if (input !== "") { + lib_core.error("unrecognized value for windows-environment"); + } + return "cygwin"; +})(); const CACHE_PREFIX = lib_core.getInput("cache-prefix", { required: false, trimWhitespace: true, @@ -147399,7 +147412,7 @@ async function acquireOpam() { } }); } -async function initializeOpam() { +async function initializeOpam(prefix) { await lib_core.group("Initialise opam state", async () => { if (PLATFORM !== "windows") { try { @@ -147416,7 +147429,7 @@ async function initializeOpam() { const extraOptions = []; if (PLATFORM === "windows") { extraOptions.push("--cygwin-local-install"); - extraOptions.push(`--cygwin-location=${CYGWIN_ROOT}`); + extraOptions.push(`--cygwin-location=${prefix}`); } if (OPAM_DISABLE_SANDBOXING) { extraOptions.push("--disable-sandboxing"); @@ -147430,9 +147443,9 @@ async function initializeOpam() { ]); }); } -async function setupOpam() { +async function setupOpam(prefix) { await acquireOpam(); - await initializeOpam(); + await initializeOpam(prefix); } async function installOcaml(ocamlCompiler) { await lib_core.group("Install OCaml", async () => { @@ -166041,6 +166054,58 @@ async function setupCygwin() { await io.cp(setup, CYGWIN_ROOT); }); } +function getPacmanPath(msys2Root) { + return external_node_path_namespaceObject.join(msys2Root, "usr", "bin", "pacman"); +} +async function testMsys2Installation(path) { + try { + await promises_namespaceObject.access(path); + } + catch { + throw new Error(`No msys2 installation found at: ${path}.`); + } +} +async function getMsys2Install() { + const msys2Root = process.env.MSYS2_ROOT; + // MSYS2_ROOT takes priority + if (msys2Root) { + await testMsys2Installation(msys2Root); + return [msys2Root, getPacmanPath(msys2Root)]; + } + try { + // check for pacman from PATH + const pacmanPath = await io.which("pacman", true); + return [external_node_path_namespaceObject.dirname(external_node_path_namespaceObject.dirname(external_node_path_namespaceObject.dirname(pacmanPath))), pacmanPath]; + } + catch { + // finally check the default msys directory + const defaultRoot = "C:\\msys64"; + await testMsys2Installation(defaultRoot); + return [defaultRoot, getPacmanPath(defaultRoot)]; + } +} +async function prepareMsys2() { + return await lib_core.group("Install needed Msys2 packages", async () => { + const [root, pacmanPath] = await getMsys2Install(); + // core update + await (0,lib_exec.exec)(pacmanPath, ["-Syu", "--noconfirm"]); + // packages needed for opam + const packages = [ + "curl", + "diffutils", + "m4", + "make", + "mingw-w64-i686-gcc", + "mingw-w64-x86_64-gcc", + "patch", + "perl", + "rsync", + "unzip", + ]; + await (0,lib_exec.exec)(pacmanPath, ["-Syu", "--noconfirm", ...packages]); + return root; + }); +} ;// CONCATENATED MODULE: ./src/cache.ts @@ -166080,6 +166145,7 @@ async function composeOpamCacheKeys() { const ocamlCompiler = await RESOLVED_COMPILER; const repositoryUrls = OPAM_REPOSITORIES.map(([_, value]) => value).join(); const osInfo = await lib.osInfo(); + const msys2 = WINDOWS_ENVIRONMENT === "msys2" ? "msys2" : undefined; const plainKey = [ PLATFORM, osInfo.release, @@ -166088,7 +166154,9 @@ async function composeOpamCacheKeys() { ocamlCompiler, repositoryUrls, sandbox, - ].join(); + ] + .concat(msys2 ?? []) + .join(); const hash = external_node_crypto_.createHash("sha256").update(plainKey).digest("hex"); const key = `${CACHE_PREFIX}-setup-ocaml-opam-${hash}`; const restoreKeys = [key]; @@ -166201,7 +166269,7 @@ async function restoreOpamCache() { } async function restoreOpamCaches() { return await lib_core.group("Retrieve the opam cache", async () => { - const [opamCacheHit, cygwinCacheHit] = await Promise.all(PLATFORM === "windows" + const [opamCacheHit, cygwinCacheHit] = await Promise.all(PLATFORM === "windows" && WINDOWS_ENVIRONMENT === "cygwin" ? [restoreOpamCache(), restoreCygwinCache()] : [restoreOpamCache()]); return { opamCacheHit, cygwinCacheHit }; @@ -166336,13 +166404,25 @@ async function installer() { } const { opamCacheHit, cygwinCacheHit } = await restoreOpamCaches(); if (PLATFORM === "windows") { - await setupCygwin(); - if (!cygwinCacheHit) { - await saveCygwinCache(); + switch (WINDOWS_ENVIRONMENT) { + case "msys2": { + const msys2Root = await prepareMsys2(); + await setupOpam(msys2Root); + break; + } + case "cygwin": + await setupCygwin(); + if (!cygwinCacheHit) { + await saveCygwinCache(); + } + lib_core.addPath(CYGWIN_ROOT_BIN); + await setupOpam(CYGWIN_ROOT); + break; } - lib_core.addPath(CYGWIN_ROOT_BIN); } - await setupOpam(); + else { + await setupOpam(); + } if (!opamCacheHit) { await repositoryRemoveAll(); await repositoryAddAll(OPAM_REPOSITORIES); diff --git a/dist/post/index.js b/dist/post/index.js index 157a8030..0b3e6e6d 100644 --- a/dist/post/index.js +++ b/dist/post/index.js @@ -112200,6 +112200,19 @@ const ALLOW_PRERELEASE_OPAM = lib_core.getBooleanInput("allow-prerelease-opam", required: false, trimWhitespace: true, }); +const constants_WINDOWS_ENVIRONMENT = (() => { + const input = lib_core.getInput("windows-environment"); + if (!(constants_PLATFORM === "windows" || input === "")) { + lib_core.error("windows-environment is only supported on windows"); + } + if (input === "msys2" || input === "cygwin") { + return input; + } + if (input !== "") { + lib_core.error("unrecognized value for windows-environment"); + } + return "cygwin"; +})(); const constants_CACHE_PREFIX = lib_core.getInput("cache-prefix", { required: false, trimWhitespace: true, @@ -112282,6 +112295,7 @@ async function composeOpamCacheKeys() { const ocamlCompiler = await RESOLVED_COMPILER; const repositoryUrls = OPAM_REPOSITORIES.map(([_, value]) => value).join(); const osInfo = await system.osInfo(); + const msys2 = WINDOWS_ENVIRONMENT === "msys2" ? "msys2" : undefined; const plainKey = [ PLATFORM, osInfo.release, @@ -112290,7 +112304,9 @@ async function composeOpamCacheKeys() { ocamlCompiler, repositoryUrls, sandbox, - ].join(); + ] + .concat(msys2 ?? []) + .join(); const hash = crypto.createHash("sha256").update(plainKey).digest("hex"); const key = `${CACHE_PREFIX}-setup-ocaml-opam-${hash}`; const restoreKeys = [key]; @@ -112403,7 +112419,7 @@ async function restoreOpamCache() { } async function restoreOpamCaches() { return await core.group("Retrieve the opam cache", async () => { - const [opamCacheHit, cygwinCacheHit] = await Promise.all(PLATFORM === "windows" + const [opamCacheHit, cygwinCacheHit] = await Promise.all(PLATFORM === "windows" && WINDOWS_ENVIRONMENT === "cygwin" ? [restoreOpamCache(), restoreCygwinCache()] : [restoreOpamCache()]); return { opamCacheHit, cygwinCacheHit }; diff --git a/packages/setup-ocaml/src/cache.ts b/packages/setup-ocaml/src/cache.ts index 13313250..4432fd26 100644 --- a/packages/setup-ocaml/src/cache.ts +++ b/packages/setup-ocaml/src/cache.ts @@ -18,6 +18,7 @@ import { OPAM_ROOT, PLATFORM, RESOLVED_COMPILER, + WINDOWS_ENVIRONMENT, } from "./constants.js"; import { retrieveLatestOpamRelease } from "./opam.js"; import { retrieveCygwinVersion } from "./windows.js"; @@ -50,6 +51,7 @@ async function composeOpamCacheKeys() { const ocamlCompiler = await RESOLVED_COMPILER; const repositoryUrls = OPAM_REPOSITORIES.map(([_, value]) => value).join(); const osInfo = await system.osInfo(); + const msys2 = WINDOWS_ENVIRONMENT === "msys2" ? "msys2" : undefined; const plainKey = [ PLATFORM, osInfo.release, @@ -58,7 +60,9 @@ async function composeOpamCacheKeys() { ocamlCompiler, repositoryUrls, sandbox, - ].join(); + ] + .concat(msys2 ?? []) + .join(); const hash = crypto.createHash("sha256").update(plainKey).digest("hex"); const key = `${CACHE_PREFIX}-setup-ocaml-opam-${hash}`; const restoreKeys = [key]; @@ -203,7 +207,7 @@ async function restoreOpamCache() { export async function restoreOpamCaches() { return await core.group("Retrieve the opam cache", async () => { const [opamCacheHit, cygwinCacheHit] = await Promise.all( - PLATFORM === "windows" + PLATFORM === "windows" && WINDOWS_ENVIRONMENT === "cygwin" ? [restoreOpamCache(), restoreCygwinCache()] : [restoreOpamCache()], ); diff --git a/packages/setup-ocaml/src/constants.ts b/packages/setup-ocaml/src/constants.ts index 23890d47..dbdeb72d 100644 --- a/packages/setup-ocaml/src/constants.ts +++ b/packages/setup-ocaml/src/constants.ts @@ -86,6 +86,23 @@ export const ALLOW_PRERELEASE_OPAM = core.getBooleanInput( }, ); +export const WINDOWS_ENVIRONMENT: "cygwin" | "msys2" = (() => { + const input = core.getInput("windows-environment"); + + if (!(PLATFORM === "windows" || input === "")) { + core.error("windows-environment is only supported on windows"); + } + + if (input === "msys2" || input === "cygwin") { + return input; + } + + if (input !== "") { + core.error("unrecognized value for windows-environment"); + } + return "cygwin"; +})(); + export const CACHE_PREFIX = core.getInput("cache-prefix", { required: false, trimWhitespace: true, diff --git a/packages/setup-ocaml/src/installer.ts b/packages/setup-ocaml/src/installer.ts index f6fc53e7..0d14032f 100644 --- a/packages/setup-ocaml/src/installer.ts +++ b/packages/setup-ocaml/src/installer.ts @@ -10,6 +10,7 @@ import { saveOpamCache, } from "./cache.js"; import { + CYGWIN_ROOT, CYGWIN_ROOT_BIN, DUNE_CACHE, DUNE_CACHE_ROOT, @@ -18,6 +19,7 @@ import { OPAM_ROOT, PLATFORM, RESOLVED_COMPILER, + WINDOWS_ENVIRONMENT, } from "./constants.js"; import { installDune } from "./dune.js"; import { @@ -29,7 +31,7 @@ import { update, } from "./opam.js"; import { retrieveOpamLocalPackages } from "./packages.js"; -import { setupCygwin } from "./windows.js"; +import { prepareMsys2, setupCygwin } from "./windows.js"; export async function installer() { if (core.isDebug()) { @@ -64,13 +66,24 @@ export async function installer() { } const { opamCacheHit, cygwinCacheHit } = await restoreOpamCaches(); if (PLATFORM === "windows") { - await setupCygwin(); - if (!cygwinCacheHit) { - await saveCygwinCache(); + switch (WINDOWS_ENVIRONMENT) { + case "msys2": { + const msys2Root = await prepareMsys2(); + await setupOpam(msys2Root); + break; + } + case "cygwin": + await setupCygwin(); + if (!cygwinCacheHit) { + await saveCygwinCache(); + } + core.addPath(CYGWIN_ROOT_BIN); + await setupOpam(CYGWIN_ROOT); + break; } - core.addPath(CYGWIN_ROOT_BIN); + } else { + await setupOpam(); } - await setupOpam(); if (!opamCacheHit) { await repositoryRemoveAll(); await repositoryAddAll(OPAM_REPOSITORIES); diff --git a/packages/setup-ocaml/src/opam.ts b/packages/setup-ocaml/src/opam.ts index 1a215271..b0ae6964 100644 --- a/packages/setup-ocaml/src/opam.ts +++ b/packages/setup-ocaml/src/opam.ts @@ -8,7 +8,6 @@ import * as semver from "semver"; import { ALLOW_PRERELEASE_OPAM, ARCHITECTURE, - CYGWIN_ROOT, GITHUB_TOKEN, OPAM_DISABLE_SANDBOXING, PLATFORM, @@ -86,7 +85,7 @@ async function acquireOpam() { }); } -async function initializeOpam() { +async function initializeOpam(prefix?: string) { await core.group("Initialise opam state", async () => { if (PLATFORM !== "windows") { try { @@ -104,7 +103,7 @@ async function initializeOpam() { const extraOptions = []; if (PLATFORM === "windows") { extraOptions.push("--cygwin-local-install"); - extraOptions.push(`--cygwin-location=${CYGWIN_ROOT}`); + extraOptions.push(`--cygwin-location=${prefix}`); } if (OPAM_DISABLE_SANDBOXING) { extraOptions.push("--disable-sandboxing"); @@ -119,9 +118,9 @@ async function initializeOpam() { }); } -export async function setupOpam() { +export async function setupOpam(prefix?: string) { await acquireOpam(); - await initializeOpam(); + await initializeOpam(prefix); } export async function installOcaml(ocamlCompiler: string) { diff --git a/packages/setup-ocaml/src/windows.ts b/packages/setup-ocaml/src/windows.ts index d519878c..44520c3d 100644 --- a/packages/setup-ocaml/src/windows.ts +++ b/packages/setup-ocaml/src/windows.ts @@ -115,3 +115,58 @@ export async function setupCygwin() { await io.cp(setup, CYGWIN_ROOT); }); } + +function getPacmanPath(msys2Root: string): string { + return path.join(msys2Root, "usr", "bin", "pacman"); +} + +async function testMsys2Installation(path: string): Promise { + try { + await fs.access(path); + } catch { + throw new Error(`No msys2 installation found at: ${path}.`); + } +} + +async function getMsys2Install(): Promise<[root: string, pacmanPath: string]> { + const msys2Root = process.env.MSYS2_ROOT; + + // MSYS2_ROOT takes priority + if (msys2Root) { + await testMsys2Installation(msys2Root); + return [msys2Root, getPacmanPath(msys2Root)]; + } + try { + // check for pacman from PATH + const pacmanPath = await io.which("pacman", true); + return [path.dirname(path.dirname(path.dirname(pacmanPath))), pacmanPath]; + } catch { + // finally check the default msys directory + const defaultRoot = "C:\\msys64"; + await testMsys2Installation(defaultRoot); + return [defaultRoot, getPacmanPath(defaultRoot)]; + } +} + +export async function prepareMsys2(): Promise { + return await core.group("Install needed Msys2 packages", async () => { + const [root, pacmanPath] = await getMsys2Install(); + // core update + await exec(pacmanPath, ["-Syu", "--noconfirm"]); + // packages needed for opam + const packages = [ + "curl", + "diffutils", + "m4", + "make", + "mingw-w64-i686-gcc", + "mingw-w64-x86_64-gcc", + "patch", + "perl", + "rsync", + "unzip", + ]; + await exec(pacmanPath, ["-Syu", "--noconfirm", ...packages]); + return root; + }); +}