From 2b2f119241d1b094fe06f81a9f08aa63d246a02d Mon Sep 17 00:00:00 2001
From: Anonymous Maarten <[EMAIL REDACTED]>
Date: Sat, 12 Aug 2023 13:24:12 +0200
Subject: [PATCH] Add cmake-arguments input
---
action.yml | 2 +
package-lock.json | 6 +
package.json | 1 +
packed/index.js | 346 ++++++++++++++++++++++++++++++++++++++++++++--
src/main.ts | 17 ++-
src/util.test.ts | 36 +++++
src/util.ts | 14 ++
7 files changed, 408 insertions(+), 14 deletions(-)
create mode 100644 src/util.test.ts
diff --git a/action.yml b/action.yml
index 19c3f39..997813b 100644
--- a/action.yml
+++ b/action.yml
@@ -20,6 +20,8 @@ inputs:
description: "Path of a CMake toolchain file"
cmake-generator:
description: "CMake generator name (see https://cmake.org/cmake/help/latest/manual/cmake-generators.7.html)"
+ cmake-arguments:
+ description: "Extra CMake arguments during configuration"
install-linux-dependencies:
description: "Install Linux dependencies"
shell:
diff --git a/package-lock.json b/package-lock.json
index 22c2b17..8ba3079 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -13,6 +13,7 @@
"@octokit/rest": "^19.0.13",
"adm-zip": "^0.5.10",
"octokit": "^2.1.0",
+ "shlex": "^2.1.2",
"uuid": "^9.0.0"
},
"devDependencies": {
@@ -7173,6 +7174,11 @@
"node": ">=8"
}
},
+ "node_modules/shlex": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/shlex/-/shlex-2.1.2.tgz",
+ "integrity": "sha512-Nz6gtibMVgYeMEhUjp2KuwAgqaJA1K155dU/HuDaEJUGgnmYfVtVZah+uerVWdH8UGnyahhDCgABbYTbs254+w=="
+ },
"node_modules/side-channel": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz",
diff --git a/package.json b/package.json
index b0de0f5..6024905 100644
--- a/package.json
+++ b/package.json
@@ -45,6 +45,7 @@
"@octokit/rest": "^19.0.13",
"adm-zip": "^0.5.10",
"octokit": "^2.1.0",
+ "shlex": "^2.1.2",
"uuid": "^9.0.0"
},
"devDependencies": {
diff --git a/packed/index.js b/packed/index.js
index a22dba5..88fabed 100644
--- a/packed/index.js
+++ b/packed/index.js
@@ -531,6 +531,9 @@ function calculate_state_hash(args) {
.digest("hex");
misc_state.push("cmake_toolchain_file_hash=".concat(cmake_toolchain_file_hash));
}
+ if (args.cmake_configure_arguments) {
+ misc_state.push("cmake_arguments=".concat(args.cmake_configure_arguments));
+ }
var complete_state = __spreadArray(__spreadArray(__spreadArray(__spreadArray(__spreadArray([
"ENVIRONMENT"
], env_state, true), [
@@ -696,7 +699,7 @@ function install_linux_dependencies(package_manager_type) {
}
function run() {
return __awaiter(this, void 0, void 0, function () {
- var GITHUB_TOKEN, OCTOKIT, SDL_BUILD_PLATFORM, SETUP_SDL_ROOT, IGNORED_SHELLS, shell_in, SHELL, REQUESTED_VERSION_TYPE, CMAKE_BUILD_TYPE, CMAKE_BUILD_TYPES, git_branch_hash, requested_version, requested_type, github_releases, release_db, sdl_release, GIT_HASH, CMAKE_TOOLCHAIN_FILE, PACKAGE_MANAGER_TYPE, STATE_HASH, PACKAGE_DIR, CACHE_KEY, CACHE_PATHS, sdl_from_cache, BUILD_SDL_TEST, SOURCE_DIR, BUILD_DIR, cmake_configure_args, CMAKE_GENERATOR, SDL_VERSION, pkg_config_path, sdl2_config;
+ var GITHUB_TOKEN, OCTOKIT, SDL_BUILD_PLATFORM, SETUP_SDL_ROOT, IGNORED_SHELLS, shell_in, SHELL, REQUESTED_VERSION_TYPE, CMAKE_BUILD_TYPE, CMAKE_BUILD_TYPES, git_branch_hash, requested_version, requested_type, github_releases, release_db, sdl_release, GIT_HASH, CMAKE_TOOLCHAIN_FILE, INPUT_CMAKE_CONFIGURE_ARGUMENTS, PACKAGE_MANAGER_TYPE, STATE_HASH, PACKAGE_DIR, CACHE_KEY, CACHE_PATHS, sdl_from_cache, BUILD_SDL_TEST, SOURCE_DIR, BUILD_DIR, cmake_configure_args, CMAKE_GENERATOR, SDL_VERSION, pkg_config_path, sdl2_config;
var _this = this;
return __generator(this, function (_a) {
switch (_a.label) {
@@ -759,12 +762,14 @@ function run() {
case 1:
GIT_HASH = _a.sent();
CMAKE_TOOLCHAIN_FILE = get_cmake_toolchain_path();
+ INPUT_CMAKE_CONFIGURE_ARGUMENTS = core.getInput("cmake-arguments");
PACKAGE_MANAGER_TYPE = parse_linux_package_manager(core.getInput("install-linux-dependencies"), SDL_BUILD_PLATFORM);
STATE_HASH = calculate_state_hash({
git_hash: GIT_HASH,
build_platform: SDL_BUILD_PLATFORM,
shell: SHELL,
cmake_toolchain_file: CMAKE_TOOLCHAIN_FILE,
+ cmake_configure_arguments: INPUT_CMAKE_CONFIGURE_ARGUMENTS,
package_manager: PACKAGE_MANAGER_TYPE,
});
PACKAGE_DIR = "".concat(SETUP_SDL_ROOT, "/").concat(STATE_HASH, "/package");
@@ -805,13 +810,8 @@ function run() {
if (PACKAGE_MANAGER_TYPE) {
install_linux_dependencies(PACKAGE_MANAGER_TYPE);
}
- cmake_configure_args = [
- "-DSDL_TEST=".concat(BUILD_SDL_TEST),
- "-DCMAKE_BUILD_TYPE=".concat(CMAKE_BUILD_TYPE),
- "-DCMAKE_INSTALL_BINDIR=bin",
- "-DCMAKE_INSTALL_INCLUDEDIR=include",
- "-DCMAKE_INSTALL_LIBDIR=lib",
- ];
+ cmake_configure_args = (0, util_1.shlex_split)(INPUT_CMAKE_CONFIGURE_ARGUMENTS);
+ cmake_configure_args.push("-DSDL_TEST=".concat(BUILD_SDL_TEST), "-DCMAKE_BUILD_TYPE=".concat(CMAKE_BUILD_TYPE), "-DCMAKE_INSTALL_BINDIR=bin", "-DCMAKE_INSTALL_INCLUDEDIR=include", "-DCMAKE_INSTALL_LIBDIR=lib");
if (CMAKE_TOOLCHAIN_FILE) {
cmake_configure_args.push("-DCMAKE_TOOLCHAIN_FILE=\"".concat(CMAKE_TOOLCHAIN_FILE, "\""));
}
@@ -982,7 +982,7 @@ exports.export_environent_variables = export_environent_variables;
/***/ }),
/***/ 9731:
-/***/ (function(__unused_webpack_module, exports) {
+/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) {
"use strict";
@@ -1002,7 +1002,8 @@ var __extends = (this && this.__extends) || (function () {
};
})();
Object.defineProperty(exports, "__esModule", ({ value: true }));
-exports.SetupSdlError = void 0;
+exports.shlex_split = exports.SetupSdlError = void 0;
+var shlex = __nccwpck_require__(5659);
var SetupSdlError = /** @class */ (function (_super) {
__extends(SetupSdlError, _super);
function SetupSdlError(message) {
@@ -1011,6 +1012,19 @@ var SetupSdlError = /** @class */ (function (_super) {
return SetupSdlError;
}(Error));
exports.SetupSdlError = SetupSdlError;
+function shlex_split(text) {
+ if (!text) {
+ return [];
+ }
+ else {
+ text = text.trim();
+ if (text == "") {
+ return [];
+ }
+ return shlex.split(text);
+ }
+}
+exports.shlex_split = shlex_split;
/***/ }),
@@ -58917,6 +58931,318 @@ function coerce (version, options) {
}
+/***/ }),
+
+/***/ 5659:
+/***/ ((__unused_webpack_module, exports) => {
+
+"use strict";
+/* eslint-disable quotes, quote-props */
+
+
+/*
+ Port of a subset of the features of CPython's shlex module, which provides a
+ shell-like lexer. Original code by Eric S. Raymond and other contributors.
+*/
+
+class Shlexer {
+ constructor (string) {
+ this.i = 0
+ this.string = string
+
+ /**
+ * Characters that will be considered whitespace and skipped. Whitespace
+ * bounds tokens. By default, includes space, tab, linefeed and carriage
+ * return.
+ */
+ this.whitespace = ' \t\r\n'
+
+ /**
+ * Characters that will be considered string quotes. The token accumulates
+ * until the same quote is encountered again (thus, different quote types
+ * protect each other as in the shell.) By default, includes ASCII single
+ * and double quotes.
+ */
+ this.quotes = `'"`
+
+ /**
+ * Characters that will be considered as escape. Just `\` by default.
+ */
+ this.escapes = '\\'
+
+ /**
+ * The subset of quote types that allow escaped characters. Just `"` by default.
+ */
+ this.escapedQuotes = '"'
+
+ /**
+ * Whether to support ANSI C-style $'' quotes
+ * https://www.gnu.org/software/bash/manual/html_node/ANSI_002dC-Quoting.html
+ */
+ this.ansiCQuotes = true
+
+ /**
+ * Whether to support localized $"" quotes
+ * https://www.gnu.org/software/bash/manual/html_node/Locale-Translation.html
+ *
+ * The behavior is as if the current locale is set to C or POSIX, i.e., the
+ * contents are not translated.
+ */
+ this.localeQuotes = true
+
+ this.debug = false
+ }
+
+ readChar () {
+ return this.string.charAt(this.i++)
+ }
+
+ processEscapes (string, quote, isAnsiCQuote) {
+ if (!isAnsiCQuote && !this.escapedQuotes.includes(quote)) {
+ // This quote type doesn't support escape sequences
+ return string
+ }
+
+ // We need to form a regex that matches any of the escape characters,
+ // without interpreting any of the characters as a regex special character.
+ const anyEscape = '[' + this.escapes.replace(/(.)/g, '\\$1') + ']'
+
+ // In regular quoted strings, we can only escape an escape character, and
+ // the quote character itself.
+ if (!isAnsiCQuote && this.escapedQuotes.includes(quote)) {
+ const re = new RegExp(
+ anyEscape + '(' + anyEscape + '|\\' + quote + ')', 'g')
+ return string.replace(re, '$1')
+ }
+
+ // ANSI C quoted strings support a wide variety of escape sequences
+ if (isAnsiCQuote) {
+ const patterns = {
+ // Literal characters
+ '([\\\\\'"?])': (x) => x,
+
+ // Non-printable ASCII characters
+ 'a': () => '\x07',
+ 'b': () => '\x08',
+ 'e|E': () => '\x1b',
+ 'f': () => '\x0c',
+ 'n': () => '\x0a',
+ 'r': () => '\x0d',
+ 't': () => '\x09',
+ 'v': () => '\x0b',
+
+ // Octal bytes
+ '([0-7]{1,3})': (x) => String.fromCharCode(parseInt(x, 8)),
+
+ // Hexadecimal bytes
+ 'x([0-9a-fA-F]{1,2})': (x) => String.fromCharCode(parseInt(x, 16)),
+
+ // Unicode code units
+ 'u([0-9a-fA-F]{1,4})': (x) => String.fromCharCode(parseInt(x, 16)),
+ 'U([0-9a-fA-F]{1,8})': (x) => String.fromCharCode(parseInt(x, 16)),
+
+ // Control characters
+ // https://en.wikipedia.org/wiki/Control_character#How_control_characters_map_to_keyboards
+ 'c(.)': (x) => {
+ if (x === '?') {
+ return '\x7f'
+ } else if (x === '@') {
+ return '\x00'
+ } else {
+ return String.fromCharCode(x.charCodeAt(0) & 31)
+ }
+ }
+ }
+
+ // Construct an uber-RegEx that catches all of the above pattern
+ const re = new RegExp(
+ anyEscape + '(' + Object.keys(patterns).join('|') + ')', 'g')
+
+ // For each match, figure out which subpattern matched, and apply the
+ // corresponding function
+ return string.replace(re, function (m, p1) {
+ for (const matched in patterns) {
+ const mm = new RegExp('^' + matched + '$').exec(p1)
+ if (mm === null) {
+ continue
+ }
+
+ return patterns[matched].apply(null, mm.slice(1))
+ }
+ })
+ }
+
+ // Should not get here
+ return undefined
+ }
+
+ * [Symbol.iterator] () {
+ let inQuote = false
+ let inDollarQuote = false
+ let escaped = false
+ let lastDollar = -2 // position of last dollar sign we saw
+ let token
+
+ if (this.debug) {
+ console.log('full input:', '>' + this.string + '<')
+ }
+
+ while (true) {
+ const pos = this.i
+ const char = this.readChar()
+
+ if (this.debug) {
+ console.log(
+ 'position:', pos,
+ 'input:', '>' + char + '<',
+ 'accumulated:', token,
+ 'inQuote:', inQuote,
+ 'inDollarQuote:', inDollarQuote,
+ 'lastDollar:', lastDollar,
+ 'escaped:', escaped
+ )
+ }
+
+ // Ran out of characters, we're done
+ if (char === '') {
+ if (inQuote) { throw new Error('Got EOF while in a quoted string') }
+ if (escaped) { throw new Error('Got EOF while in an escape sequence') }
+ if (token !== undefined) { yield token }
+ return
+ }
+
+ // We were in an escape sequence, complete it
+ if (escaped) {
+ if (char === '\n') {
+ // An escaped newline just means to continue the command on the next
+ // line. We just need to ignore it.
+ } else if (inQuote) {
+ // If we are in a quote, just accumulate the whole escape sequence,
+ // as we will interpret escape sequences later.
+ token = (token || '') + escaped + char
+ } else {
+ // Just use the literal character
+ token = (token || '') + char
+ }
+
+ escaped = false
+ continue
+ }
+
+ if (this.escapes.includes(char)) {
+ if (!inQuote || inDollarQuote !== false || this.escapedQuotes.includes(inQuote)) {
+ // We encountered an escape character, which is going to affect how
+ // we treat the next character.
+ escaped = char
+ continue
+ } else {
+ // This string type doesn't use escape characters. Ignore for now.
+ }
+ }
+
+ // We were in a string
+ if (inQuote !== false) {
+ // String is finished. Don't grab the quote character.
+ if (char === inQuote) {
+ token = this.processEscapes(token, inQuote, inDollarQuote === '\'')
+ inQuote = false
+ inDollarQuote = false
+ continue
+ }
+
+ // String isn't finished yet, accumulate the character
+ token = (token || '') + char
+ continue
+ }
+
+ // This is the start of a new string, don't accumulate the quotation mark
+ if (this.quotes.includes(char)) {
+ inQuote = char
+ if (lastDollar === pos - 1) {
+ if (char === '\'' && !this.ansiCQuotes) {
+ // Feature not enabled
+ } else if (char === '"' && !this.localeQuotes) {
+ // Feature not enabled
+ } else {
+ inDollarQuote = char
+ }
+ }
+
+ token = (token || '') // fixes blank string
+
+ if (inDollarQuote !== false) {
+ // Drop the opening $ we captured before
+ token = token.slice(0, -1)
+ }
+
+ continue
+ }
+
+ // This is a dollar sign, record that we saw it in case it's the start of
+ // an ANSI C or localized string
+ if (inQuote === false && char === '$') {
+ lastDollar = pos
+ }
+
+ // This is whitespace, so yield the token if we have one
+ if (this.whitespace.includes(char)) {
+ if (token !== undefined) { yield token }
+ token = undefined
+ continue
+ }
+
+ // Otherwise, accumulate the character
+ token = (token || '') + char
+ }
+ }
+}
+
+
+/**
+ * Splits a given string using shell-like syntax. This function is the inverse
+ * of shlex.join().
+ *
+ * @param {String} s String to split.
+ * @returns {String[]}
+ */
+exports.split = function (s) {
+ return Array.from(new Shlexer(s))
+}
+
+/**
+ * Escapes a potentially shell-unsafe string using quotes.
+ *
+ * @param {String} s String to quote
+ * @returns {String}
+ */
+exports.quote = function (s) {
+ if (s === '') { return '\'\'' }
+
+ const unsafeRe = /[^\w@%\-+=:,./]/
+ if (!unsafeRe.test(s)) { return s }
+
+ return ('\'' + s.replace(/('+)/g, '\'"$1"\'') + '\'').replace(/^''|''$/g, '')
+}
+
+
+/**
+ * Concatenate the tokens of the list args and return a string. This function
+ * is the inverse of shlex.split().
+ *
+ * The returned value is shell-escaped to protect against injection
+ * vulnerabilities (see shlex.quote()).
+ *
+ * @param {String[]} args List of args to join
+ * @returns {String}
+*/
+exports.join = function (args) {
+ if (!Array.isArray(args)) {
+ throw new TypeError("args should be an array")
+ }
+ return args.map(exports.quote).join(" ")
+}
+
+
/***/ }),
/***/ 4256:
diff --git a/src/main.ts b/src/main.ts
index 78501d2..e2d0a2d 100644
--- a/src/main.ts
+++ b/src/main.ts
@@ -10,7 +10,7 @@ import { Octokit } from "@octokit/rest";
import AdmZip = require("adm-zip");
import { SDL_GIT_REPO } from "./constants";
-import { SetupSdlError } from "./util";
+import { SetupSdlError, shlex_split } from "./util";
import * as linuxpm from "./linuxpm";
import {
@@ -199,6 +199,7 @@ function calculate_state_hash(args: {
build_platform: SdlBuildPlatform;
shell: string;
cmake_toolchain_file: string | undefined;
+ cmake_configure_arguments: string | undefined;
package_manager: linuxpm.PackageManagerType | undefined;
}) {
const ENV_KEYS = [
@@ -259,6 +260,10 @@ function calculate_state_hash(args: {
misc_state.push(`cmake_toolchain_file_hash=${cmake_toolchain_file_hash}`);
}
+ if (args.cmake_configure_arguments) {
+ misc_state.push(`cmake_arguments=${args.cmake_configure_arguments}`);
+ }
+
const complete_state: string[] = [
"ENVIRONMENT",
...env_state,
@@ -512,6 +517,7 @@ async function run() {
});
const CMAKE_TOOLCHAIN_FILE = get_cmake_toolchain_path();
+ const INPUT_CMAKE_CONFIGURE_ARGUMENTS = core.getInput("cmake-arguments");
const PACKAGE_MANAGER_TYPE = parse_linux_package_manager(
core.getInput("install-linux-dependencies"),
@@ -523,6 +529,7 @@ async function run() {
build_platform: SDL_BUILD_PLATFORM,
shell: SHELL,
cmake_toolchain_file: CMAKE_TOOLCHAIN_FILE,
+ cmake_configure_arguments: INPUT_CMAKE_CONFIGURE_ARGUMENTS,
package_manager: PACKAGE_MANAGER_TYPE,
});
@@ -567,13 +574,15 @@ async function run() {
install_linux_dependencies(PACKAGE_MANAGER_TYPE);
}
- const cmake_configure_args = [
+ const cmake_configure_args = shlex_split(INPUT_CMAKE_CONFIGURE_ARGUMENTS);
+
+ cmake_configure_args.push(
`-DSDL_TEST=${BUILD_SDL_TEST}`,
`-DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}`,
"-DCMAKE_INSTALL_BINDIR=bin",
"-DCMAKE_INSTALL_INCLUDEDIR=include",
- "-DCMAKE_INSTALL_LIBDIR=lib",
- ];
+ "-DCMAKE_INSTALL_LIBDIR=lib"
+ );
if (CMAKE_TOOLCHAIN_FILE) {
cmake_configure_args.push(
`-DCMAKE_TOOLCHAIN_FILE="${CMAKE_TOOLCHAIN_FILE}"`
diff --git a/src/util.test.ts b/src/util.test.ts
new file mode 100644
index 0000000..bd32871
--- /dev/null
+++ b/src/util.test.ts
@@ -0,0 +1,36 @@
+import { shlex_split } from "./util";
+
+import { describe, expect, test } from "@jest/globals";
+
+describe("testing shlex.parse", () => {
+ test("test undefined", () => {
+ expect(shlex_split(undefined)).toStrictEqual([]);
+ });
+ test("test empty string", () => {
+ expect(shlex_split("")).toStrictEqual([]);
+ });
+ test("test string with whitespace(s)", () => {
+ expect(shlex_split(" ")).toStrictEqual([]);
+ expect(shlex_split(" ")).toStrictEqual([]);
+ expect(shlex_split("\t")).toStrictEqual([]);
+ expect(shlex_split(" \t")).toStrictEqual([]);
+ expect(shlex_split("\t\t \t")).toStrictEqual([]);
+ });
+ test("test simple string with text", () => {
+ expect(shlex_split("a")).toStrictEqual(["a"]);
+ expect(shlex_split("a b")).toStrictEqual(["a", "b"]);
+ expect(shlex_split(" a \t \t b ")).toStrictEqual(["a", "b"]);
+ });
+ test("test string with escape characters", () => {
+ expect(shlex_split('"a"')).toStrictEqual(["a"]);
+ expect(shlex_split('"a" "b"')).toStrictEqual(["a", "b"]);
+ expect(shlex_split('"a b" ')).toStrictEqual(["a b"]);
+ });
+ test("test example extra cmake arguments", () => {
+ expect(shlex_split("-A win32")).toStrictEqual(["-A", "win32"]);
+ expect(shlex_split("-DSDL_STATIC=ON -DSDL_X11=OFF")).toStrictEqual([
+ "-DSDL_STATIC=ON",
+ "-DSDL_X11=OFF",
+ ]);
+ });
+});
diff --git a/src/util.ts b/src/util.ts
index 20e0239..caf7d50 100644
--- a/src/util.ts
+++ b/src/util.ts
@@ -1,5 +1,19 @@
+import shlex = require("shlex");
+
export class SetupSdlError extends Error {
constructor(message: string) {
super(message);
}
}
+
+export function shlex_split(text: undefined | string): string[] {
+ if (!text) {
+ return [];
+ } else {
+ text = text.trim();
+ if (text == "") {
+ return [];
+ }
+ return shlex.split(text);
+ }
+}