From c26134e0fd94c83a7ec64ff61acf133802ea4eb3 Mon Sep 17 00:00:00 2001 From: Alejandro R Mosteo Date: Wed, 6 May 2020 16:03:51 +0200 Subject: [PATCH] Load `environment` properties (#389) * Document `environment` property * Load/Store `environment` property * Retrieve all env vars set by a release * Test: load/show environment properties * Quote var names as ${name} * Update new .md documentation --- doc/catalog-format-spec.md | 26 + doc/catalog-format-spec.rst | 531 ++++++++++++++++++ src/alire/alire-properties-environment.adb | 153 +++++ src/alire/alire-properties-environment.ads | 47 ++ src/alire/alire-properties-from_toml.ads | 24 +- src/alire/alire-releases.adb | 17 + src/alire/alire-releases.ads | 11 +- src/alire/alire-toml_adapters.adb | 32 ++ src/alire/alire-toml_adapters.ads | 8 + src/alire/alire-toml_keys.ads | 1 + .../environment/my_index/index/he/hello.toml | 16 + .../environment/my_index/index/index.toml | 1 + testsuite/tests/index/environment/test.py | 31 + testsuite/tests/index/environment/test.yaml | 4 + 14 files changed, 894 insertions(+), 8 deletions(-) create mode 100644 doc/catalog-format-spec.rst create mode 100644 src/alire/alire-properties-environment.adb create mode 100644 src/alire/alire-properties-environment.ads create mode 100644 testsuite/tests/index/environment/my_index/index/he/hello.toml create mode 100644 testsuite/tests/index/environment/my_index/index/index.toml create mode 100644 testsuite/tests/index/environment/test.py create mode 100644 testsuite/tests/index/environment/test.yaml diff --git a/doc/catalog-format-spec.md b/doc/catalog-format-spec.md index bf4e4a4c..c21a4b2e 100644 --- a/doc/catalog-format-spec.md +++ b/doc/catalog-format-spec.md @@ -276,6 +276,32 @@ entries: windows = { OS = "ms-linux" } # to see all enumeration values, one per row. ``` + - `environment`: optional table used to modify environment variables that + will apply at build time. Variables and values are specified with the form + `VARIABLE. = "value"`, where `` is one of `append`, + `prepend`, or `set`. For instance: + + ```toml + [environment] + C_INCLUDE_PATH.append = "/usr/include/something" + PATH.prepend = "${CRATE_ROOT}/bin" + MSYS.set = "${MSYS_ROOT}" + ``` + + Other environment variables may appear in the value, and will be replaced. + Furthermore, a few predefined variables are provided by Alire: + + - `${CRATE_ROOT}` is the deployment directory of the crate. + - `${MSYS_ROOT}` is available on Windows to obtain the detected location of + an MSYS2 installation. + + Environment entries can use dynamic expressions: + + ```toml + [environment.'case(os)'] + windows = { C_INCLUDE_PATH.append = "${MSYS_ROOT}/mingw64/include/SDL2" } + ``` + - `executables`: optional list of strings. Each one is the simple name of an executable provided by the package. Executables are looked for by `alr` in the build tree and must not include a path. If only one executable is given, diff --git a/doc/catalog-format-spec.rst b/doc/catalog-format-spec.rst new file mode 100644 index 00000000..11a666c8 --- /dev/null +++ b/doc/catalog-format-spec.rst @@ -0,0 +1,531 @@ +Catalog format specification +============================ + +Big picture +----------- + +Each project is described as a TOML file. For instance: ``aaa.toml`` for the +AAA project, ``gnatcoll.toml`` for the package corresponding to GNATCOLL. + +Each TOML description contains an object that has a ``general`` table and then +one table per release. + +.. code-block:: toml + + [general] + ... + + ['1.0'] + ... + + ['1.1'] + ... + + ['2.0'] + ... + +``general`` contains information common to all releases, then each release +provides sources (``origin``) and can refine common information. + + +Information encoding +-------------------- + +This section describes the various encodings used in this format to encode +information. + +First, there are two kinds of data: atomic and composite data. + +Atomic data designates values that cannot be decomposed. There are only two +atomic data types: + +* mere strings (``"Hello, world!"``); +* booleans (``true``, ``false``); + +We can then split composite data in two kinds: lists (TOML's arrays) and +mappings (JSON's tables). Lists are just sequences of other values; for +instance a list of strings: + +.. code-block:: toml + + ["A", "B"] + +Mapping are the traditional sets of associations from keys (here, always +strings) to other values. For instance, the following represents a set of +dependencies, with version constraints: + +.. code-block:: toml + + libfoo = "^1.2" + libbar = "^2.0 & /=2.1.3" # Excluding a known bad version + +In some contexts, information can be dynamic: special encodings can be used to +make data vary depending on the environment (OS, architecture, …). The +environment is represented as a set of specific variables which can have +a specific set of values: see the Parameters section below for a comprehensive +list. + +To create atomic values in an environment-dependent way, use the following +construct (here to create a boolean): + +.. code-block:: toml + + {'case(distribution)' = { + 'debian|ubuntu': true, + '...': false + }} + + # Or in a more idiomatic TOML syntay + ['case(distribution)'] + 'debian|ubuntu' = true + '...' = false + +Depending on the value of the ``distribution`` environment variable, this will +return ``true`` (its value is ``debian`` or ``ubuntu``) or ``false`` (for other +values). + +A little variation allows to build environment-dependent composite data. For +instance, to make the dependency on ``libbar`` above dynamic: + +.. code-block:: toml + + { + "libfoo": "^1.2", + "case(os)": { + "linux": {"libbar": "^2.0"}, + "windows": {"libwinbar": "^3.0"}, + "...": {} + } + } + + # Or in a more idiomatic TOML syntay + libfoo = "^1.2" + + ['case(os)'.linux] + libbar = "^2.0" + + ['case(os)'.windows] + libwinbar = "^3.0" + + ['case(os)'.'...'] + +If the ``os`` environment variable contains ``linux``, this will create the +following dependencies: + +.. code-block:: toml + + libfoo = "^1.2" + libbar = "^2.0" + +The ``case(os)`` part selects dependencies depending on the value of the ``os`` +environment variable. + +.. code-block:: toml + + libfoo = "^1.2" + libwinbar = "^3.0" + +And finally for other ``os`` values: + +.. code-block:: toml + + libfoo = "^1.2" + + +General Information +------------------- + +Unless specified, all the entries must be static, i.e. they cannot depend on +the context. + +The ``general`` entry must contain an object. It contains itself the following +entries: + +* ``description``: mandatory string. One-line description about the package. + For instance: + + .. code-block:: toml + + description = "Library to handle foobars" + + +* ``long-description``: optional free-form string to provide information about + this package, in addition to ``description``, without length restrictions. + +* ``authors``: optional array of strings. Flat list of human-readable names for + the authors, i.e. the people that wrote the software that is packaged. For + instance: + + .. code-block:: toml + + authors = ["Alice Example", + "Bob For Instance "] + +* ``maintainers``: mandatory array of strings. Flat list of human-readable + names (optional) for the maintainers, with a contact email (mandatory); i.e. + the people that maintain the crate metadata in Alire. For instance: + + .. code-block:: toml + + maintainers = ["alice@example.com", + "Bob For Instance "] + +* ``maintainers-logins``: mandatory array of strings. Flat list of github login + usernames used by the maintainers of the crate. This information is used to + authorize crate modifications. For instance: + + .. code-block:: toml + + maintainers-logins = ["alicehacks", "bobcoder"] + +* ``licenses``: mandatory array of strings. Flat list of licenses for the + software that is packaged. The following licenses are allowed: + + * ``AFL 3.0``, ``AGPL 3.0``, ``Apache 2.0``, ``Artistic 2.0``, + ``BSD 2-Clauses``, ``BSD 3-Clauses Clear``, ``BSD 3-Clauses``, + ``BSL 1.0``, ``CC0 1.0``, ``CC BY 4.0``, ``CC BY-SA 4.0``, + ``ECL 2.0``, ``EPL 1.0``, ``EPL 2.0``, ``EUPL 1.1``, ``EUPL 1.2``, + ``GPL 2.0``, ``GPL 3.0``, ``ISC``, ``LGPL 2.1``, ``LGPL 3.0``, + ``LPPL 1.3c``, ``MIT``, ``MPL 2.0``, ``MS PL``, ``MS RL``, ``NCSA``, + ``OFL 1.1``, ``OSL 3.0``, ``PostgreSQL``, ``Unlicense``, ``WTFPL``, + ``zlib``, ``GMGPL 2.0``, ``GMGPL 3.0``, ``Public Domain``. + + * ``Custom``, can be used for project specific licenses. + + If the license is unknown or not in the list above, leave an empty array. + + .. code-block:: toml + + licenses = [] + + For a double license: + + .. code-block:: toml + + licenses = ["GPL 3.0", "MIT"] + + +* ``website``: optional string. URL to the original project's website. For + instance: + + .. code-block:: toml + + website = "https://myproject.example.org/" + +* ``tags``: optional array of strings. Flat list of topics covered by the + crate. Tags will help users find crates reletaed to their interests: + + .. code-block:: toml + + tags = ["spark", "security"] + +* ``available``: optional dynamic boolean expression. Determines whether the + package is available for the current platform (true) or not (false). For + instance: + + .. code-block:: toml + + [available.'case(distribution)'] + 'debian|ubuntu' = true + '...' = false + +* ``depends-on``: optional dynamic dependencies expression common to all + releases. For instance: + + .. code-block:: toml + + [depends-on] + libfoo = "^1.2" + + [depends-on.'case(os)'.linux] + libbar = "^2.0" + + [depends-on.'case(os)'.windows] + libwinbar = "^3.0" + + Available constraint operators are the usual Ada ones (=, /=, >, >=, <, <=) + plus caret (^, any upwards version within the same major point) and tilde + (~, any upwards version within the same minor point). Logical operators for + and (&), or (|) are accepted; see the ``Semantic_Versioning`` project + documentation on `extended version sets + `_. + +* ``project-files``: optional list of strings. Each is a path, relative to the + root of the source directory, to a project file to be made available. + Expressions are accepted. For instance: + + .. code-block:: toml + + project-files = ["my_project.gpr", "utils/utils_for_my_project.gpr"] + + [project-files.'case(word-size)'] + bits-64 = ["my_project.gpr"] + bits-32 = ["my_project32.gpr"] + +* ``gpr-externals``: optional table, giving a mapping from the name of external + variables in the project files to sets of possible values (as array of + strings), or an empty string if this set is infinite. For instance: + + .. code-block:: toml + + [gpr-externals] + BUILD_MODE = ["debug", "profile", "release"] + TAG = "" + +* ``gpr-set-externals``: optional table, giving a mapping from the name of + external variables to the values to use by default when building the project. + Expressions are accepted before the mapping. For instance: + + .. code-block:: toml + + [gpr-set-externals] + BUILD_MODE = "release" + + [gpr-set-externals.'case(os)'] + linux = { OS = "gnu-linux" } # Compact table syntax is convenient in this case + windows = { OS = "ms-linux" } # to see all enumeration values, one per row. + +* ``environment``: optional table used to modify environment variables that + will apply at build time. Variables and values are specified with the form + ``VARIABLE. = "value"``, where ```` is one of ``append``, + ``prepend``, or ``set``. For instance: + + .. code-block:: toml + + [environment] + C_INCLUDE_PATH.append = "/usr/include/something" + PATH.prepend = "${CRATE_ROOT}/bin" + MSYS.set = "${MSYS_ROOT}" + + Other environment variables may appear in the value, and will be replaced. + Furthermore, a few predefined variables are provided by Alire: + + * ``${CRATE_ROOT}`` is the deployment directory of the crate. + * ``${MSYS_ROOT}`` is available on Windows to obtain the detected location of + an MSYS2 installation. + + Environment entries can use dynamic expressions: + + .. code-block:: toml + + [environment.'case(os)'] + windows = { C_INCLUDE_PATH.append = "${MSYS_ROOT}/mingw64/include/SDL2" } + +* ``executables``: optional list of strings. Each one is the simple name of an + executable provided by the package. Executables are looked for by ``alr`` in the + build tree and must not include a path. If only one executable is given, it is + considered the default for ``alr run``. For instance: + + .. code-block:: toml + + executables = ["my_main"] + +* ``actions``: optional list of actions to perform when installing this package. + The general action syntax is: + + .. code-block:: toml + + [[actions]] + type = + command = + + ```` is a an array of strings for a shell command to run in the + source directory. ```` can be either: + + * ``post-fetch``: the command is to be run right after getting the package + sources; + * ``post-compile``: the command is to be run right after GPRbuild has been + run. + + Actions accept dynamic expressions. For example: + + .. code-block:: toml + + [[general.actions.'case(os)'.linux]] + type = "post-fetch" + command = ["make"] + + [[general.actions.'case(os)'.windows]] + type = "post-fetch" + command = ["cmd", "build"] + + [[general.actions.'case(os)'.'...']] + # An explicit empty case alternative, which is not mandatory + +Release-specific Information +---------------------------- + +Each release is materialized as an entry in the top-level object. The key is a +string for the version number for the release, while the value is an object to +contain the release-specific information. This object can contains the +following entries: + +* ``origin``: mandatory dynamic string expression. URL used to fetch the + sources to build. For instance: + + .. code-block:: toml + + # Clone a git repository + origin = "git+https://github.com/example-user/example-project" + + # Download and extract a source archive + origin = "https://example.org/archive.tar.gz" + + If the package only maps a package from the system package manager, (for + instance ``make``), run: + + .. code-block:: json + + origin = "native:make" + + Make the expression evaluate to an empty string to mean that the package is + not available, or just leave the alternative out. For instance, to state that + ``make`` is available on Debian/Ubuntu and not on the other platforms: + + .. code-block:: json + + [origin.'case(distribution)'] + 'debian|ubuntu' = "native:make" + +* ``origin-hashes``: mandatory string array for git origins and source archives. + An array of "kind:digest" fields that specify a hash kind and its value. + Kinds accepted are: sha512. + +* ``archive-name``: optional string. If ``origin`` points to a source archive, + this can specifiy the name of the file to download, which is needed in order + to properly extract the sources. For instance: + + .. code-block:: json + + origin = "https://example.org/0123456789" + archive-name = "archive.tar.gz" + archive-hash = "sha512:bf6082573dc537836ea8506a2c9a75dc7837440c35c5b02a52add52e38293640d99e90a9706690591f8899b8b4935824b195f230b3aa1c4da10911e3caf954c04ac" + +* ``available``: optional dynamic boolean expression. It is used the following + way: + + 1. If it evaluates to ``false``, the package is not available for the current + platform. + 2. Otherwise, the availability is determined by the ``available`` entry in + the ``general`` section. + +* ``notes``: optional string. Provides miscellanous information about this + release. For instance: + + .. code-block:: json + + "notes": "Experimental version" + +It can also contain the following entries: ``depends-on``, ``project-files``, +``gpr-externals``, ``gpr-set-externals``, ``executables``, ``actions``. These +are optional. For atomic values, these override the ones from ``general``, and +for lists/mappings, they are interpreted as additions. In the latter case, +conflicting entries are considered as errors. + +External Releases +----------------- + +The above information applies to regular releases distributed from sources +(that is, the Ada projects whose distribution is the main Alire goal). Some +special supporting releases also exist that are described differently. + +A release is considered "external" when it is not built from sources and, +furthermore, its semantic version cannot be known until run time. Hence, the +availability and version of these releases is detected by ``alr``. + +Several definitions for these external releases may exist, and so they are +defined in the index as a vector with key ``external``: + +.. code-block:: json + + [[external]] + # Common entries to all externals + kind = "hint" # One of several predefined external kinds + hint = "Please install SDL in your platform from source or system packages" + # Specific external kind parameters might follow + +All external kinds can define these regular properties: + +* ``available``: when defined, it restricts the external detection to the + given environment conditions. + +* ``hint``: explanation for the user on how to make the external available. + This explanation is show on request with ``alr show --external``, or after + ``alr get``, for any external dependency that could not be detected. This + property accepts dynamic expressions. + +External kinds: hints +^^^^^^^^^^^^^^^^^^^^^ + +A plain undetectable external intended to simply serve as a hint. For crates +that are known to be unavailable through Alire, it serves to provide a generic +or customized hint to the user. It has no specific fields, other than the +common ones just described. Its key is ``"hint"``: + +.. code-block:: json + + [[external]] + kind = "hint" # Identifies this external kind + # Bare minimum external. Optionally, the hint/available fields can be used. + +External kinds: command-line tools +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +This external kind is used to describe commands that can be run in the system, +and that are able to provide their own version via some particular invocation. +Their specific fields are (all mandatory): + +.. code-block:: json + + kind = "version-output" # Identifies this external kind + + version-command = ["gnat", "--version"] + # Invocation that will provide the version when the tool is available + + version-regexp = "^GNAT ([\\d\\.]+).*|^GNAT Community ([\\d]{4}).*" + # TOML-escaped GNAT.Regpat-compatible regular expression. Parenthesized + # matches will cause the matched expression to be parsed as the Semantic + # Version of the tool. + +External kinds: system packages +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Systems that have their own package manager (e.g. Linux) can readily provide +many complex dependencies still unpackaged as source code in Alire. Alire can +use these on supported platforms (at this time, Debian & Ubuntu. Do not +hesitate to contact us if you would like to maintain other ones distributions) +during resolution. + +A system external gives a list of platform package names that supply the +dependency natively. The platform package manager will be used to detect their +availability and version. To that effect, the ``origin`` field is used (which +can accept dynamic expressions in this context): + +.. code-block:: json + + kind = "system" # Identifies this external kind + origin = ["libncursesada3", "libncursesada5"] + # As versions appear this list will grow. To speed up detection, dynamic + # expressions may become recommendable for certain system packages. + +For Ada precompiled system libraries that require the platform compiler for +linking (e.g., in Debian/Ubuntu), and that cannot be used with other GNAT +compilers, this should be expressed with the ``available`` property, e.g.: + +.. code-block:: json + + available.'case(toolchain)'.user = false + # `available` defaults to true, so it is enough to flag the user toolchains + +Parameters +---------- + +* ``os``: name of the OS. Currently supported values are: ``linux``, ``macos`` + and ``windows``. +* ``distribution``: name of the Linux distribution, or ``none`` if running on a + different OS. Currently supported values are: ``debian``, ``ubuntu``. +* ``toolchain``: takes ``system`` value in distributions with the system Ada + compiler first in PATH (GNAT FSF in Debian/Ubuntu), ``user`` otherwise + (GNAT Community editions, other cross-target toolchains). +* ``word-size``: architecture word size. Currently supported values are: + ``bits-32``, ``bits-64``, ``bits-unknown`` diff --git a/src/alire/alire-properties-environment.adb b/src/alire/alire-properties-environment.adb new file mode 100644 index 00000000..0d253b5a --- /dev/null +++ b/src/alire/alire-properties-environment.adb @@ -0,0 +1,153 @@ +with Alire.TOML_Keys; +with Alire.Utils; + +package body Alire.Properties.Environment is + + ------------ + -- Action -- + ------------ + + function Action (This : Variable) return Actions is + (This.Action); + + ---------- + -- Name -- + ---------- + + function Name (This : Variable) return String is + (+This.Name); + + function Shell_Name (This : Variable) return String is + ("${" & Name (This) & "}"); + + ----------- + -- Value -- + ----------- + + function Value (This : Variable) return String is + (+This.Value); + + --------------- + -- Image_RHS -- + --------------- + + function Image_RHS (This : Variable) return String is + (Name (This) + & (case This.Action is + when Append => "=" & Shell_Name (This) & ":" & Value (This), + when Prepend => "=" & Value (This) & ":" & Shell_Name (This), + when Set => "=" & Value (This))); + + ----------- + -- Image -- + ----------- + + overriding + function Image (This : Variable) return String is + ("Environment: " & Image_RHS (This)); + + --------- + -- Key -- + --------- + + overriding + function Key (This : Variable) return String is + pragma Unreferenced (This); + begin + return TOML_Keys.Environment; + end Key; + + --------------- + -- From_TOML -- + --------------- + + function From_TOML (From : TOML_Adapters.Key_Queue) + return Conditional.Properties + is + use type Conditional.Properties; + use TOML; + Env : TOML_Value; + begin + if From.Unwrap.Kind /= TOML_Table then + From.Checked_Error + ("environment: table with assignments expected, but got: " + & From.Unwrap.Kind'Img); + end if; + + if From.Pop_Single_Table (Env, TOML_Table) /= TOML_Keys.Environment then + raise Program_Error; + -- Can't happen, unless the dispatch to us itself was erroneous + end if; + + return Props : Conditional.Properties do + for Name of Env.Keys loop + declare + Var : Variable; -- The env. var. being parsed + Val : TOML_Value; -- The env. var. value + begin + Var.Name := Name; + + -- Action + + declare + Action_Image : constant String := + From.Descend + (Value => Env.Get (Name), + Context => "environment: " & (+Name)) + .Pop_Single_Table (Val, TOML_String); + begin + Var.Action := Actions'Value (Action_Image); + exception + when Constraint_Error => -- because Action is invalid + From.Checked_Error + (": " & (+Var.Name) + & "invalid action: " & Action_Image); + end; + + -- Value (already type checked in previous pop) + + Var.Value := +Val.As_String; + + -- Pop entry to avoid upper "unexpected key" errors + + Env.Unset (+Name); + + -- Final assignment + + Props := Props and Var; + end; + end loop; + + end return; + end From_TOML; + + ------------- + -- To_TOML -- + ------------- + + overriding + function To_TOML (This : Variable) return TOML.TOML_Value is + use TOML; + Child : constant TOML_Value := Create_Table; + begin + return Result : constant TOML_Value := Create_Table do + + -- Create the VAR.action.Value nested tables + + Child.Set (Utils.To_Lower_Case (This.Action'Img), + Create_String (Value (This))); + + Result.Set (Name (This), + Child); + end return; + end To_TOML; + + ------------- + -- To_YAML -- + ------------- + + overriding + function To_YAML (This : Variable) return String is + ("Environment: '" & Image_RHS (This) & "'"); + +end Alire.Properties.Environment; diff --git a/src/alire/alire-properties-environment.ads b/src/alire/alire-properties-environment.ads new file mode 100644 index 00000000..f71a0082 --- /dev/null +++ b/src/alire/alire-properties-environment.ads @@ -0,0 +1,47 @@ +with Alire.Conditional; +with Alire.TOML_Adapters; + +package Alire.Properties.Environment with Preelaborate is + + type Actions is (Append, Prepend, Set); + + type Variable is new Property with private; + + -- Own data + + function Action (This : Variable) return Actions; + + function Name (This : Variable) return String; + -- Returns the name as is. See Shell_Name below. + + function Shell_Name (This : Variable) return String; + -- Returns the name as it appears in "shell" expressions: ${NAME} + + function Value (This : Variable) return String; + + -- Inherited operations + + overriding + function Image (This : Variable) return String; + + overriding + function Key (This : Variable) return String; + + function From_TOML (From : TOML_Adapters.Key_Queue) + return Conditional.Properties; + + overriding + function To_TOML (This : Variable) return TOML.TOML_Value; + + overriding + function To_YAML (This : Variable) return String; + +private + + type Variable is new Property with record + Action : Actions; + Name : UString; + Value : UString; + end record; + +end Alire.Properties.Environment; diff --git a/src/alire/alire-properties-from_toml.ads b/src/alire/alire-properties-from_toml.ads index bdbd7a22..abd542d6 100644 --- a/src/alire/alire-properties-from_toml.ads +++ b/src/alire/alire-properties-from_toml.ads @@ -1,6 +1,7 @@ with Alire.Actions; with Alire.Conditional; with Alire.Crates; +with Alire.Properties.Environment; with Alire.Properties.Labeled; with Alire.Properties.Licenses; with Alire.Properties.Scenarios; @@ -13,6 +14,7 @@ package Alire.Properties.From_TOML with Preelaborate is type Property_Keys is (Actions, Authors, Description, + Environment, Executables, GPR_Externals, GPR_Set_Externals, @@ -29,8 +31,10 @@ package Alire.Properties.From_TOML with Preelaborate is type Loader_Array is array (Property_Keys range <>) of Property_Loader; - -- We use these arrays to determine which properties may appear when - -- loading a [general] section or a proper release. + -- We use these arrays to determine which properties may appear in each + -- crate section. These loaders will always receive a table of the form + -- prop_name = prop_value. Dynamic expressions are resolved prior to + -- dispatching to these loaders, so they need not to care. External_Loaders : constant Loader_Array (Property_Keys) := (Hint => Labeled.From_TOML'Access, @@ -38,16 +42,20 @@ package Alire.Properties.From_TOML with Preelaborate is -- This loader is used for properties common to all external classes General_Loaders : constant Loader_Array (Property_Keys) := - (Actions => Alire.Actions.From_TOML'Access, + (Actions => Alire.Actions.From_TOML'Access, + Environment => Properties.Environment.From_TOML'Access, GPR_Externals .. GPR_Set_Externals - => Scenarios.From_TOML'Access, - Hint => null, -- Only apply to externals - Licenses => Properties.Licenses.From_TOML'Access, - others => Labeled.From_TOML'Access); + => Scenarios.From_TOML'Access, + Hint => null, -- Only apply to externals + Licenses => Properties.Licenses.From_TOML'Access, + others => Labeled.From_TOML'Access); + -- This loader is used in the [general] crate section Release_Loaders : constant Loader_Array (Property_Keys) := (Actions => Alire.Actions.From_TOML'Access, + Environment => + Properties.Environment.From_TOML'Access, Executables => Labeled.From_TOML'Access, GPR_Externals .. GPR_Set_Externals @@ -55,12 +63,14 @@ package Alire.Properties.From_TOML with Preelaborate is Notes => Labeled.From_TOML'Access, Project_Files => Labeled.From_TOML'Access, others => null); + -- This loader applies to release sections -- The following array determines which properties accept dynamic -- expressions, per index semantics. All other properties must be static. Loaders_During_Case : constant array (Property_Keys) of Property_Loader := (Actions => Alire.Actions.From_TOML'Access, + Environment => Properties.Environment.From_TOML'Access, Executables => Labeled.From_TOML_Executable_Cases'Access, GPR_Set_Externals => Scenarios.From_TOML_Cases'Access, Hint => Labeled.From_TOML_Hint_Cases'Access, diff --git a/src/alire/alire-releases.adb b/src/alire/alire-releases.adb index c74bfa21..603d29dc 100644 --- a/src/alire/alire-releases.adb +++ b/src/alire/alire-releases.adb @@ -306,6 +306,23 @@ package body Alire.Releases is end return; end Props_To_Strings; + ----------------- + -- Environment -- + ----------------- + + function Environment (R : Release; + P : Alire.Properties.Vector) return Env_Maps.Map + is + package Env renames Alire.Properties.Environment; + begin + return Map : Env_Maps.Map do + for Prop of R.On_Platform_Properties (P, Env.Variable'Tag) loop + Map.Insert (Env.Variable (Prop).Name, + Env.Variable (Prop)); + end loop; + end return; + end Environment; + ----------------- -- Executables -- ---------------- diff --git a/src/alire/alire-releases.ads b/src/alire/alire-releases.ads index fa19e7b5..bc284b25 100644 --- a/src/alire/alire-releases.ads +++ b/src/alire/alire-releases.ads @@ -1,3 +1,4 @@ +with Ada.Containers.Indefinite_Ordered_Maps; with Ada.Tags; with Alire.Actions; @@ -7,7 +8,7 @@ with Alire.Interfaces; with Alire.Milestones; with Alire.Origins; with Alire.Crates; -with Alire.Properties; +with Alire.Properties.Environment; with Alire.Properties.Labeled; with Alire.Properties.Licenses; with Alire.Requisites; @@ -155,6 +156,14 @@ package Alire.Releases with Preelaborate is function Default_Executable (R : Release) return String; -- We encapsulate here the fixing of platform extension + package Env_Maps is new Ada.Containers.Indefinite_Ordered_Maps + (String, Alire.Properties.Environment.Variable, + "<", Alire.Properties.Environment."="); + + function Environment (R : Release; + P : Alire.Properties.Vector) return Env_Maps.Map; + -- Retrieve env vars that are set by this release, key is the var name + function Executables (R : Release; P : Alire.Properties.Vector) return Utils.String_Vector; diff --git a/src/alire/alire-toml_adapters.adb b/src/alire/alire-toml_adapters.adb index fb6d4e46..f2caf33b 100644 --- a/src/alire/alire-toml_adapters.adb +++ b/src/alire/alire-toml_adapters.adb @@ -159,6 +159,38 @@ package body Alire.TOML_Adapters is return ""; end Pop_Expr; + ---------------------- + -- Pop_Single_Table -- + ---------------------- + + function Pop_Single_Table (Queue : Key_Queue; + Value : out TOML.TOML_Value; + Kind : TOML.Any_Value_Kind) return String + is + use TOML; + begin + if Queue.Value.Kind /= TOML_Table then + Queue.Checked_Error ("expected a table but got a " + & Queue.Value.Kind'Img); + end if; + + if Queue.Value.Keys'Length /= 1 then + Queue.Checked_Error ("expected a single entry in table, but got" + & Queue.Value.Keys'Length'Img); + end if; + + Value := Queue.Value.Get (Queue.Value.Keys (1)); + + if Value.Kind /= Kind then + Queue.Checked_Error ("expected a single entry of type " + & Kind'Img & ", but got a " & Value.Kind'Img); + end if; + + return Key : constant String := +Queue.Value.Keys (1) do + Queue.Value.Unset (Queue.Value.Keys (1)); + end return; + end Pop_Single_Table; + ----------------------- -- Report_Extra_Keys -- ----------------------- diff --git a/src/alire/alire-toml_adapters.ads b/src/alire/alire-toml_adapters.ads index 7d684d62..217210b2 100644 --- a/src/alire/alire-toml_adapters.ads +++ b/src/alire/alire-toml_adapters.ads @@ -77,6 +77,14 @@ package Alire.TOML_Adapters with Preelaborate is -- or No_TOML_Value if not a table or does not contain such a key. The -- intended use is to process keys beginning with "case(" in the table. + function Pop_Single_Table (Queue : Key_Queue; + Value : out TOML.TOML_Value; + Kind : TOML.Any_Value_Kind) return String; + -- For constructions like [parent.child.grandchild], where we known that + -- only one child can exist. Will raise Checked_Error if any of these + -- happens: Queue is not a table; Queue doesn't have exactly one key; + -- Value is not of the expected Kind. Returns the single Key. + function Unwrap (Queue : Key_Queue) return TOML.TOML_Value; -- Return the internal value as-is (with any already popped keys missing). diff --git a/src/alire/alire-toml_keys.ads b/src/alire/alire-toml_keys.ads index fbb046ee..4300c187 100644 --- a/src/alire/alire-toml_keys.ads +++ b/src/alire/alire-toml_keys.ads @@ -13,6 +13,7 @@ package Alire.TOML_Keys with Preelaborate is Depends_On : constant String := "depends-on"; Description : constant String := "description"; Distribution : constant String := "distribution"; + Environment : constant String := "environment"; Executable : constant String := "executables"; External : constant String := "external"; External_Kind : constant String := "kind"; diff --git a/testsuite/tests/index/environment/my_index/index/he/hello.toml b/testsuite/tests/index/environment/my_index/index/he/hello.toml new file mode 100644 index 00000000..65f8a646 --- /dev/null +++ b/testsuite/tests/index/environment/my_index/index/he/hello.toml @@ -0,0 +1,16 @@ +[general] +description = """"Hello, world!" demonstration project""" +licenses = [] +maintainers = ["example@example.com"] +maintainers-logins = ["mylogin"] + +[general.environment] +VAR1.append = "abc" +VAR2.prepend = "xyz" +VAR3.set = "pqr" + +[general.environment.'case(os)'.'...'] +CONDVAR.set = "uvw" + +[1] +origin = "file://../blah.zip" diff --git a/testsuite/tests/index/environment/my_index/index/index.toml b/testsuite/tests/index/environment/my_index/index/index.toml new file mode 100644 index 00000000..7c969026 --- /dev/null +++ b/testsuite/tests/index/environment/my_index/index/index.toml @@ -0,0 +1 @@ +version = "0.2" diff --git a/testsuite/tests/index/environment/test.py b/testsuite/tests/index/environment/test.py new file mode 100644 index 00000000..554d1243 --- /dev/null +++ b/testsuite/tests/index/environment/test.py @@ -0,0 +1,31 @@ +""" +Test proper loading of environment properties +""" + +from drivers.alr import run_alr +from drivers.asserts import assert_match + +import re + + +# With conditionals +p = run_alr('show', 'hello') + +assert_match('.*' + ' when Linux => \(Environment: CONDVAR=uvw\)\n' + '.*' + ' Environment: VAR1=\${VAR1}:abc\n' + ' Environment: VAR2=xyz:\${VAR2}\n' + ' Environment: VAR3=pqr\n' + '.*', + p.out, flags=re.S) + +# Check resolved conditional +p = run_alr('show', 'hello', '--system') + +assert_match('.*' + ' Environment: CONDVAR=uvw\n' + '.*', + p.out, flags=re.S) + +print('SUCCESS') diff --git a/testsuite/tests/index/environment/test.yaml b/testsuite/tests/index/environment/test.yaml new file mode 100644 index 00000000..0a859639 --- /dev/null +++ b/testsuite/tests/index/environment/test.yaml @@ -0,0 +1,4 @@ +driver: python-script +indexes: + my_index: + in_fixtures: false -- 2.39.5