diff options
43 files changed, 1337 insertions, 0 deletions
diff --git a/README.md b/README.md new file mode 100644 index 0000000..c2f4892 --- /dev/null +++ b/README.md @@ -0,0 +1,123 @@ +## Rule `["CC", "library"]` + +A C++ library + +| Field | Description | +| ----- | ----------- | +| `"name"` | The name of the library (without leading `"lib"` or trailing `".a"`). | +| `"stage"` | The logical location of all header and source files, as well as the resulting library file. Individual directory components are joined with `"/"`. | +| `"pure C"` | If non-empty, compile as C sources rathter than C++ sources. In particular, CC is used to compile rather than CXX (or their respective defaults). | +| `"defines"` | List of defines set for this target and its consumers. Each list entry will be prepended by `"-D"`. | +| `"private-defines"` | List of defines set for source files local to this target. Each list entry will be prepended by `"-D"`. | +| `"cflags"` | List of compile flags set for this target and its consumers. | +| `"private-cflags"` | List of compile flags set for source files local to this target. | +| `"private-ldflags"` | Additional linker flags for linking external libraries (not built by this tool, typically system libraries). | +| `"srcs"` | The source files of the library. | +| `"hdrs"` | Any public header files of the library. | +| `"private-hdrs"` | Any header files that only need to be present when compiling the source files, but are not needed for any consumer of the library. | +| `"deps"` | Any other libraries this library depends upon. | +| `"private-deps"` | Any other libraries this library depends upon but does not include in its public headers. | +| `"proto"` | Any `["proto", "library"]` this target depends upon directly. The creation of C++ bindings for this proto library as well as of its dependencies will be taken care of (as anonymous targets, so no duplicate work will be carried out, even if the same proto library is used at various places). | + +## Rule `["CC", "binary"]` + +A binary written in C++ + +| Field | Description | +| ----- | ----------- | +| `"name"` | The name of the binary | +| `"stage"` | The logical location of all header and source files, as well as the resulting binary file. Individual directory components are joined with `"/"`. | +| `"pure C"` | If non-empty, compile as C sources rathter than C++ sources. In particular, CC is used to compile rather than CXX | +| `"private-defines"` | List of defines set for source files local to this target. Each list entry will be prepended by `"-D"`. | +| `"private-cflags"` | List of compile flags set for source files local to this target. | +| `"private-ldflags"` | Additional linker flags for linking external libraries. | +| `"srcs"` | The source files of the library. | +| `"private-hdrs"` | Any header files that need to be present when compiling the source files. | +| `"private-deps"` | Any other libraries this binary depends upon. | +| `"private-proto"` | Any `["proto", "library"]` this target depends upon directly. The creation of C++ bindings for this proto library as well as of is dependencies will be taken care of (as anonymous targets, so no duplicate work will be carried out, even if the same proto library is used at various places). | + +## Rule `["CC/test", "test"]` + +A test written in C++ + +| Field | Description | +| ----- | ----------- | +| `"name"` | The name of the test Used to name the test binary as well as for staging the test result | +| `"args"` | Command line arguments for the test binary | +| `"stage"` | The logical location of all header and source files. Individual directory components are joined with `"/"`. | +| `"private-defines"` | List of defines set for source files local to this target. Each list entry will be prepended by `"-D"`. | +| `"private-cflags"` | List of compile flags set for source files local to this target. | +| `"private-ldflags"` | Additional linker flags for linking external libraries (not built by this tool, typically system libraries). | +| `"srcs"` | The sources of the test binary | +| `"private-hdrs"` | Any additional header files that need to be present when compiling the test binary. | +| `"data"` | Any files the test binary needs access to when running | + +## Rule `["shell/test", "script"]` + +Shell test, given by a test script + +| Field | Description | +| ----- | ----------- | +| `"keep"` | List of names (relative to the test working directory) of files that the test might generate that should be kept as part of the output. This might be useful for further analysis of the test | +| `"name"` | A name for the test, used in reporting, as well as for staging the test result tree in the runfiles | +| `"deps"` | Any targets that should be staged (with artifacts and runfiles) into the tests working directory | +| `"test"` | The shell script for the test, launched with sh. An empty directory is created to store any temporary files needed by the test, and it is made available in the environment variable TEST_TMPDIR. The test should not assume write permissions outside the working directory and the TEST_TMPDIR. For convenience, the environment variable TMPDIR is also set to TEST_TMPDIR. | + +## Rule `["proto", "library"]` + +A proto library as abtract data structure. + + Such a libray does not produce any artifacts itself, but it can be used as a dependency for various language-specific rules. + +| Field | Description | +| ----- | ----------- | +| `"stage"` | The directory to stage the source files to. Directory components are joined by `"/"`. | +| `"name"` | The name of the (abstract) library. | +| `"service"` | If non empty, generate a service library (with acces sto `"rpc"` definitions) instead of a regular one. | +| `"srcs"` | The proto files for this library | +| `"deps"` | Any other proto library this library depends on | + +## Rule `["data", "staged"]` + +Stage data to a logical subdirectory. + +| Field | Description | +| ----- | ----------- | +| `"stage"` | The logical directory to stage the files to. Individual directory components are joined with `"/"`. | +| `"srcs"` | The files to be staged | +| `"deps"` | Targets of with their runfiles should be added as well. Their staging is not changed. | + +## Rule `["patch", "file"]` + +Replace a file, logically in place, by a patched version + +| Field | Description | +| ----- | ----------- | +| `"patch-part"` | If the patch contains hunks for multiple files, only apply hunks for the specified file path. Individual directory components are joined with `"/"`. Note that the patch must be provided in unified format. | +| `"src"` | The single source file to patch, typically an explict file reference. | +| `"patch"` | The patch to apply. | + +## Rule `["CC/auto", "config"]` + +Generate a C/C++ config header + + Generate a C/C++ configuration header using defines specified via the target configuration. In the usual case, a target using this rule is configured by depending on it from a target that uses the built-in `"configure"` rule. + +| Field | Description | +| ----- | ----------- | +| `"name"` | Name of the header file to generate (incl. file name ext). | +| `"stage"` | The location of the header. Path segments are joined with `"/"`. | +| `"guard"` | The include guard. Multiple segments are joined with `"_"`. | +| `"hdrs"` | Additional header files to be available in the include path. Useful for providing additional header files to values given in `"have_{cfile,cxxfile,ctype,cxxtype,csymbol,cxxsymbol}"`. | +| `"deps"` | Additional public header files from targets to be available in the include path. Useful for providing additional header files to values given in `"have_{cfile,cxxfile,ctype,cxxtype,csymbol,cxxsymbol}"`. | + +## Rule `["CC/IDE", "headers"]` + +Transitive public headers of C++ target + +| Field | Description | +| ----- | ----------- | +| `"stage"` | The logical location of the header files. Individual directory components are joined with `"/"`. | +| `"proto"` | The proto source files for creating cc bindings. | +| `"deps"` | The targets to obtain the headers from. | + diff --git a/etc/generate-doc.sh b/etc/generate-doc.sh new file mode 100755 index 0000000..ccf379f --- /dev/null +++ b/etc/generate-doc.sh @@ -0,0 +1,57 @@ +#!/bin/sh +# Copyright 2022 Huawei Cloud Computing Technology Co., Ltd. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +set -eu + +readonly ROOT=$(readlink -f $(dirname $0)/..) + +: ${OUTFILE:=README.md} + +doc2md() { + local MAIN="$1" + local MODULE="$2" + local RULE="$3" + + local RULE_DOC="$(just-mr --main "$MAIN" describe --rule --json "$MODULE" "$RULE")" + local DOC="$(echo "$RULE_DOC" | jq -r '.doc')" + local FIELD_DOC="$(echo "$RULE_DOC" | jq -r '.field_doc')" + + echo "## Rule \`[\"$MODULE\", \"$RULE\"]\`" + echo + echo "$DOC" \ + | jq -r '[.[] as $v | if ($v == "") then "\n\n" else $v end] | join(" ")' \ + | sed 's/\("[^"]*"\|\[[^]]*\]\|{[^}]*}\)/`\1`/g' + echo + echo "| Field | Description |" + echo "| ----- | ----------- |" + echo "$FIELD_DOC" \ + | jq -r 'keys_unsorted[] as $k | "| \"\($k)\" | \(.[$k] | join(" ")) |"' \ + | sed 's/\("[^"]*"\|\[[^]]*\]\|{[^}]*}\)/`\1`/g' + echo +} + +rm -f "$OUTFILE" +( doc2md rules CC library + doc2md rules CC binary + doc2md rules CC/test test + doc2md rules shell/test script + doc2md rules proto library + doc2md rules data staged + doc2md rules patch file + doc2md rules CC/auto config + doc2md rules CC/IDE headers +) >> "$OUTFILE" + + diff --git a/etc/generate-repos.sh b/etc/generate-repos.sh new file mode 100755 index 0000000..43a58c4 --- /dev/null +++ b/etc/generate-repos.sh @@ -0,0 +1,25 @@ +#!/bin/sh +# Copyright 2022 Huawei Cloud Computing Technology Co., Ltd. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +set -eu + +readonly ROOT=$(readlink -f $(dirname $0)/..) + +: ${BRANCH:=v1.0.0} +: ${OUTFILE:=${ROOT}/etc/repos.json} + +just-import-git --as just -C ${ROOT}/etc/repos.template.json \ + -b ${BRANCH} https://github.com/just-buildsystem/justbuild \ + | ${ROOT}/etc/json-format.py > ${OUTFILE} diff --git a/etc/imports/rules.TARGETS b/etc/imports/rules.TARGETS new file mode 100644 index 0000000..73e1ff5 --- /dev/null +++ b/etc/imports/rules.TARGETS @@ -0,0 +1 @@ +{"tree": {"type": "install", "dirs": [[["TREE", null, "."], "rules"]], "tainted": ["test"]}} diff --git a/etc/json-format.py b/etc/json-format.py new file mode 100755 index 0000000..058d098 --- /dev/null +++ b/etc/json-format.py @@ -0,0 +1,72 @@ +#!/usr/bin/env python3 + +# Copyright (C) 2021-2022 Klaus Aehlig. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +import json +import sys + +def is_simple(entry): + if isinstance(entry, list): + return len(entry) == 0 + if isinstance(entry, dict): + return len(entry) == 0 + return True + +def is_short(entry, indent): + return len(json.dumps(entry)) + indent < 80 + +def hdumps(entry, *, _current_indent=0): + if is_short(entry, _current_indent): + return json.dumps(entry) + if isinstance(entry, list) and entry: + result = "[ " + hdumps(entry[0], _current_indent=_current_indent+2) + for x in entry[1:]: + result += "\n" + " " * _current_indent + ", " + result += hdumps(x, _current_indent=_current_indent+2) + result += "\n" + " " * _current_indent + "]" + return result + if isinstance(entry, dict) and entry: + result = "{ " + is_first = True + for k in entry.keys(): + if not is_first: + result += "\n" + " " * _current_indent + ", " + result += json.dumps(k) + ":" + if is_simple(entry[k]): + result += " " + json.dumps(entry[k]) + elif is_short(entry[k], _current_indent + len(json.dumps(k)) + 4): + result += " " + json.dumps(entry[k]) + else: + result += "\n" + " " * _current_indent + " " + result += hdumps(entry[k], _current_indent=_current_indent+2) + is_first = False + result += "\n" + " " * _current_indent + "}" + return result + return json.dumps(entry) + +if __name__ == "__main__": + data = json.load(sys.stdin) + print(hdumps(data)) diff --git a/etc/repos.json b/etc/repos.json new file mode 100644 index 0000000..0e26865 --- /dev/null +++ b/etc/repos.json @@ -0,0 +1,393 @@ +{ "main": "rules" +, "repositories": + { "rules": + { "repository": {"type": "file", "path": "rules"} + , "bindings": + {"protoc": "just/protobuf", "grpc": "just/com_github_grpc_grpc"} + } + , "tests": + { "repository": {"type": "file", "path": "tests"} + , "bindings": {"test-just": "just", "test-rules": "test-rules"} + } + , "imports": {"repository": {"type": "file", "path": "etc/imports"}} + , "test-rules": + { "repository": {"type": "file", "path": "rules"} + , "target_root": "imports" + , "target_file_name": "rules.TARGETS" + } + , "just": + { "repository": + { "type": "git" + , "repository": "https://github.com/just-buildsystem/justbuild" + , "branch": "v1.0.0" + , "commit": "c29b671f798e82ba26b5f54ebc9e24c7dcfb8166" + } + , "bindings": + { "rules": "just/rules-just" + , "just-distfiles": "just/just-distfiles" + , "gsl-lite": "just/gsl-lite" + , "cli11": "just/cli11" + , "json": "just/json" + , "fmt": "just/fmt" + , "ssl": "just/ssl" + , "grpc": "just/com_github_grpc_grpc" + , "googleapis": "just/google_apis" + , "bazel_remote_apis": "just/bazel_remote_apis" + , "libgit2": "just/com_github_libgit2_libgit2" + , "catch2": "just/catch2" + , "protoc": "just/protobuf" + } + } + , "just/bazel_remote_apis": + { "repository": + { "type": "archive" + , "content": "b5deb95d544f03f1918cc9d611c7904b8173befa" + , "fetch": "https://github.com/bazelbuild/remote-apis/archive/v2.0.0.tar.gz" + , "sha256": "79204ed1fa385c03b5235f65b25ced6ac51cf4b00e45e1157beca6a28bdb8043" + , "sha512": "7465f4726a9cc263352f6f0ec5b065c53e2ad787611740e1a458331858bdf28d53d69ba3db3d83fbf4a578229a413ac230886b5fcd33ab76425c61b1f08ef3b3" + , "subdir": "remote-apis-2.0.0" + } + , "target_root": "just/import targets" + , "target_file_name": "TARGETS.bazel_remote_apis" + , "bindings": + { "rules": "just/rules-protobuf" + , "protoc": "just/protobuf" + , "google_apis": "just/google_apis" + } + } + , "just/catch2": + { "repository": + { "type": "archive" + , "content": "a154ae9e2daad22c95cff6186b18637d4e5f338c" + , "fetch": "https://github.com/catchorg/Catch2/archive/v2.13.1.tar.gz" + , "sha256": "36bcc9e6190923961be11e589d747e606515de95f10779e29853cfeae560bd6c" + , "sha512": "2c5394d4ca8346d7d64203048f4ba503c2f813aa2ea7d065ffb9c63f532b7f18daee3e1a4a37314575e33f14259182edd1db030ed254b97a2e3f11d295555397" + , "subdir": "Catch2-2.13.1/single_include/catch2" + } + , "target_root": "just/import targets" + , "target_file_name": "TARGETS.catch2" + , "bindings": {"rules": "just/rules"} + } + , "just/cli11": + { "repository": + { "type": "archive" + , "content": "58c912141164a5c0f0139bfa91343fefe151d525" + , "fetch": "https://github.com/CLIUtils/CLI11/archive/refs/tags/v2.2.0.tar.gz" + , "sha256": "d60440dc4d43255f872d174e416705f56ba40589f6eb07727f76376fb8378fd6" + , "sha512": "ee8994c99dd8119e612be0339252b863d6db9d85a10c15a6e60e56c6f387bbefaf479679bc85aed2cb6539aeb82b3e7543673a5b97eb04b61793b6946582241d" + , "subdir": "CLI11-2.2.0/" + } + , "target_root": "just/import targets" + , "target_file_name": "TARGETS.cli11" + , "bindings": {"rules": "just/rules"} + } + , "just/com_github_cares_cares": + { "repository": + { "type": "archive" + , "content": "229bb6835455e73a550e4dc44f8ddac22dc34aa7" + , "fetch": "https://github.com/c-ares/c-ares/archive/e982924acee7f7313b4baa4ee5ec000c5e373c30.tar.gz" + , "sha256": "e8c2751ddc70fed9dc6f999acd92e232d5846f009ee1674f8aee81f19b2b915a" + , "sha512": "c213e2a15e5e8adfc3036d66537d4c9ca6b5685b1d0e7a57fe1abe44eebb99d5e0cb42f6a9259fd8ca75d1a4833dbc1009e2025e633b871b902b02f172fcc9bd" + , "subdir": "c-ares-e982924acee7f7313b4baa4ee5ec000c5e373c30" + } + , "target_root": "just/import targets" + , "target_file_name": "TARGETS.cares" + , "bindings": {"rules": "just/rules", "grpc": "just/com_github_grpc_grpc"} + } + , "just/com_github_grpc_grpc": + { "repository": + { "type": "archive" + , "content": "23f49d3b842f2e916c861d5150e4b7d048084888" + , "fetch": "https://github.com/grpc/grpc/archive/v1.31.0.tar.gz" + , "sha256": "1236514199d3deb111a6dd7f6092f67617cd2b147f7eda7adbafccea95de7381" + , "sha512": "fc68079a70c39d66cb6f028e775418a90c61694406ddfa413298ec33de2f56e26eb47e10a53fc616e32c254c84e335598dc22598a95c223698ebf8eca60f7cea" + , "subdir": "grpc-1.31.0" + } + , "target_root": "just/import targets" + , "target_file_name": "TARGETS.grpc" + , "bindings": + { "rules": "just/rules-grpc" + , "protobuf": "just/protobuf" + , "libssl": "just/ssl" + , "absl": "just/com_google_absl" + , "upb": "just/upb" + , "zlib": "just/zlib" + , "re2": "just/re2" + , "cares": "just/com_github_cares_cares" + } + } + , "just/com_github_libgit2_libgit2": + { "repository": + { "type": "archive" + , "content": "15b9b9ac0236534922b46c301b0f791413ac8bae" + , "fetch": "https://github.com/libgit2/libgit2/releases/download/v1.1.0/libgit2-1.1.0.tar.gz" + , "sha256": "ad73f845965cfd528e70f654e428073121a3fa0dc23caac81a1b1300277d4dba" + , "sha512": "a5226fbb11648168611a6daab978df59b68a3b5d809410d3e5dac6c04de5d962cdbabfbec7b0b6528bed94fe321d94c546748b7a180949f2ab30bb7c3487c9bc" + , "subdir": "libgit2-1.1.0" + } + , "target_root": "just/import targets" + , "target_file_name": "TARGETS.git2" + , "bindings": + { "rules": "just/rules-git2" + , "zlib": "just/zlib" + , "ssl": "just/ssl" + , "patches": "just/patches" + } + } + , "just/com_google_absl": + { "repository": + { "type": "archive" + , "content": "d9ba22c59e08577e0986c6d483f33c9fa7b2e104" + , "fetch": "https://github.com/abseil/abseil-cpp/archive/df3ea785d8c30a9503321a3d35ee7d35808f190d.tar.gz" + , "sha256": "f368a8476f4e2e0eccf8a7318b98dafbe30b2600f4e3cf52636e5eb145aba06a" + , "sha512": "b429758c37749d3d5c5c220fe0dc00fa0e193b406545484095339b25674c4a3bb7850b76dd1a370ed2006729bcbbbb1b438995b614e149c2290bdb10038c49d1" + , "subdir": "abseil-cpp-df3ea785d8c30a9503321a3d35ee7d35808f190d" + } + , "target_root": "just/import targets" + , "target_file_name": "TARGETS.absl" + , "bindings": {"rules": "just/rules-absl"} + } + , "just/fmt": + { "repository": + { "type": "zip" + , "content": "939f915e9957eda2194ecf8874149e903c99d071" + , "fetch": "https://github.com/fmtlib/fmt/releases/download/7.0.3/fmt-7.0.3.zip" + , "sha256": "decfdf9ad274070fa85f26407b816f5a4d82205ae86bac1990be658d0795ea4d" + , "sha512": "377efc454d902d925c2f889107ceb2dc7ed1c813f0035f91448f388558a732fd3df101161c679c23a950d3687cc5bf69eedee2bd90f41edd0b220e466759d62b" + , "subdir": "fmt-7.0.3" + } + , "target_root": "just/import targets" + , "target_file_name": "TARGETS.fmt" + , "bindings": {"rules": "just/rules"} + } + , "just/google_apis": + { "repository": + { "type": "zip" + , "content": "8a1ca602cb7eb42094c853f0640489599962c2cc" + , "fetch": "https://github.com/googleapis/googleapis/archive/143084a2624b6591ee1f9d23e7f5241856642f4d.zip" + , "sha256": "7b6ea252f0b8fb5cd722f45feb83e115b689909bbb6a393a873b6cbad4ceae1d" + , "sha512": "319bd988d25877bc72ab7fad8309126268f88d987a64b9ffb7956bdd442938e02ec922251de933aba8d424559fd8dc43cf7954f6c865e3f863940c1e8f533c5a" + , "subdir": "googleapis-143084a2624b6591ee1f9d23e7f5241856642f4d" + } + , "target_root": "just/import targets" + , "target_file_name": "TARGETS.google_apis" + , "bindings": + { "rules": "just/rules-protobuf" + , "protoc": "just/protobuf" + , "patches": "just/patches" + } + } + , "just/gsl-lite": + { "repository": + { "type": "archive" + , "content": "ecbc51f342f7ad97ed4c236f36d2fb2279240d7b" + , "distfile": "0.37.0.tar.gz" + , "fetch": "https://github.com/gsl-lite/gsl-lite/archive/0.37.0.tar.gz" + , "sha256": "a31d51b73742bb234acab8d2411223cf299e760ed713f0840ffed0dabe57ca38" + , "sha512": "7cc94a252933bb9c66d58387e2093173c7aa810f854b38b5a9148dafb20346a9a01351f528c4e987f6abf919132dc4bd50189a58d543b476635d6f20887c7543" + , "subdir": "gsl-lite-0.37.0/include/gsl" + } + , "target_root": "just/import targets" + , "target_file_name": "TARGETS.gsl" + , "bindings": {"rules": "just/rules"} + } + , "just/json": + { "repository": + { "type": "zip" + , "content": "eb7ab4ad48f9fb6758cff4a39b76f35abead5881" + , "fetch": "https://github.com/nlohmann/json/releases/download/v3.9.1/include.zip" + , "sha256": "6bea5877b1541d353bd77bdfbdb2696333ae5ed8f9e8cc22df657192218cad91" + , "sha512": "24984da33c5bf80eb276712d4bdc698c2724e72dc0f4c70e87527fb6b16e21f535f5a022d52c7ed2f59dcfe4a4e5b61a56101b61def09b31c827689f6c7ec673" + , "subdir": "include/nlohmann" + } + , "target_root": "just/import targets" + , "target_file_name": "TARGETS.json" + , "bindings": {"rules": "just/rules"} + } + , "just/just-distfiles": + { "repository": + { "type": "distdir" + , "repositories": + [ "just/gsl-lite" + , "just/cli11" + , "just/json" + , "just/fmt" + , "just/ssl" + , "just/protobuf" + , "just/bazel_remote_apis" + , "just/google_apis" + , "just/upb" + , "just/com_google_absl" + , "just/zlib" + , "just/re2" + , "just/com_github_cares_cares" + , "just/com_github_grpc_grpc" + , "just/com_github_libgit2_libgit2" + , "just/catch2" + ] + } + , "target_root": "just/import targets" + , "target_file_name": "TARGETS.distfiles" + } + , "just/patches": + { "repository": + { "type": "git" + , "repository": "https://github.com/just-buildsystem/justbuild" + , "branch": "v1.0.0" + , "commit": "c29b671f798e82ba26b5f54ebc9e24c7dcfb8166" + , "subdir": "etc/patches" + } + } + , "just/protobuf": + { "repository": + { "type": "zip" + , "content": "7af7165b585e4aed714555a747b6822376176ef4" + , "fetch": "https://github.com/protocolbuffers/protobuf/archive/refs/tags/v3.12.4.zip" + , "sha256": "c5dc4cacbb303d5d0aa20c5cbb5cb88ef82ac61641c951cdf6b8e054184c5e22" + , "sha512": "11e7584f44994ed6e9745bf6cbbac791bb090615d362fb3bd9aa8ebc08504042303ff945007030274359e268f8d62e116eaba2bb4910431ea3f7845af23cc7c5" + , "subdir": "protobuf-3.12.4" + } + , "target_root": "just/import targets" + , "target_file_name": "TARGETS.protobuf" + , "bindings": {"rules": "just/rules-protobuf", "zlib": "just/zlib"} + } + , "just/re2": + { "repository": + { "type": "archive" + , "content": "987bf33d9e876431f4ab3c630ff08605f58b98a7" + , "fetch": "https://github.com/google/re2/archive/aecba11114cf1fac5497aeb844b6966106de3eb6.tar.gz" + , "sha256": "9f385e146410a8150b6f4cb1a57eab7ec806ced48d427554b1e754877ff26c3e" + , "sha512": "bbe972e3fd65da69e0d7ac0e0b025fb09ad894315cb6c0e1268fa296599274f9f7c2e771aa577b340c8fa9412d8539fe3db2171d0e9ab9714f3f10910a1d5529" + , "subdir": "re2-aecba11114cf1fac5497aeb844b6966106de3eb6" + } + , "target_root": "just/import targets" + , "target_file_name": "TARGETS.re2" + , "bindings": {"rules": "just/rules-re2"} + } + , "just/rules": + { "repository": + { "type": "git" + , "repository": "https://github.com/just-buildsystem/justbuild" + , "branch": "v1.0.0" + , "commit": "c29b671f798e82ba26b5f54ebc9e24c7dcfb8166" + , "subdir": "rules" + } + , "target_root": "just/defaults" + , "rule_root": "just/rules" + } + , "just/rules-absl": + { "repository": "just/rules" + , "target_root": "just/defaults" + , "rule_root": "just/rules" + , "target_file_name": "TARGETS.absl" + , "bindings": {"base": "just/rules"} + } + , "just/rules-boringssl": + { "repository": "just/rules" + , "target_root": "just/defaults" + , "rule_root": "just/rules" + , "target_file_name": "TARGETS.boringssl" + , "bindings": {"base": "just/rules"} + } + , "just/rules-git2": + { "repository": "just/rules" + , "target_root": "just/defaults" + , "rule_root": "just/rules" + , "target_file_name": "TARGETS.git2" + , "bindings": {"base": "just/rules"} + } + , "just/rules-grpc": + { "repository": "just/rules" + , "target_root": "just/defaults" + , "rule_root": "just/rules" + , "target_file_name": "TARGETS.grpc" + , "bindings": {"base": "just/rules", "protoc": "just/protobuf"} + } + , "just/rules-just": + { "repository": "just/rules" + , "target_root": "just/defaults" + , "rule_root": "just/rules" + , "target_file_name": "TARGETS.just" + , "bindings": + { "base": "just/rules" + , "protoc": "just/protobuf" + , "grpc": "just/com_github_grpc_grpc" + } + } + , "just/rules-protobuf": + { "repository": "just/rules" + , "target_root": "just/defaults" + , "rule_root": "just/rules" + , "target_file_name": "TARGETS.protobuf" + , "bindings": + { "base": "just/rules" + , "protoc": "just/protobuf" + , "grpc": "just/com_github_grpc_grpc" + } + } + , "just/rules-re2": + { "repository": "just/rules" + , "target_root": "just/defaults" + , "rule_root": "just/rules" + , "target_file_name": "TARGETS.re2" + , "bindings": {"base": "just/rules"} + } + , "just/ssl": + { "repository": + { "type": "archive" + , "content": "cdf51ff27d78e1aceb7cc01d03f9a115826501be" + , "fetch": "https://github.com/google/boringssl/archive/e8a935e323510419e0b37638716f6df4dcbbe6f6.tar.gz" + , "sha256": "5bbb2bbddf5e4e5fefd02501f930436f3f45402152d7ea9f8f27916d5cf70157" + , "sha512": "c1cb6a94b967985e05c699ff73d1e6aebba27903d771c58008cedbbdead53eda33e112de10691af7471e823013afada466ea1abb420a3d55cfd8e2a4b09effed" + , "subdir": "boringssl-e8a935e323510419e0b37638716f6df4dcbbe6f6" + } + , "target_root": "just/import targets" + , "target_file_name": "TARGETS.boringssl" + , "bindings": {"rules": "just/rules-boringssl"} + } + , "just/upb": + { "repository": + { "type": "archive" + , "content": "a234f5d2ccff01ee0a36e016b482276c5078905d" + , "fetch": "https://github.com/protocolbuffers/upb/archive/92e63da73328d01b417cf26c2de7b0a27a0f83af.tar.gz" + , "sha256": "79f7de61203c4ee5e4fcb2f17c5f3338119d6eb94aca8bce05332d2c1cfee108" + , "sha512": "9121c15ba540dbee1e09896a42b88b8616616929c4e607dbbc2f8706a8119672f34a0e81968b1d1d62b532d22bda969a32d22755b086456d031e2689aec7f24f" + , "subdir": "upb-92e63da73328d01b417cf26c2de7b0a27a0f83af" + } + , "target_root": "just/import targets" + , "target_file_name": "TARGETS.upb" + , "bindings": {"rules": "just/rules"} + } + , "just/zlib": + { "repository": + { "type": "archive" + , "content": "c47b5e6e3db9dd9f5dfec2ba28428a0444d1c052" + , "fetch": "https://github.com/madler/zlib/archive/cacf7f1d4e3d44d871b605da3b647f07d718623f.tar.gz" + , "sha256": "6d4d6640ca3121620995ee255945161821218752b551a1a180f4215f7d124d45" + , "sha512": "c6a9e08d7cb11ed90faf40335f5378ae92cf3d973edd96be40a5dd46e9b2ab5fa26acea666ee0d3e3c69e3c7d7d709702b82ace809e59972447386c9955cf280" + , "subdir": "zlib-cacf7f1d4e3d44d871b605da3b647f07d718623f" + } + , "target_root": "just/import targets" + , "target_file_name": "TARGETS.zlib" + , "bindings": {"rules": "just/rules"} + } + , "just/defaults": + { "repository": + { "type": "git" + , "repository": "https://github.com/just-buildsystem/justbuild" + , "branch": "v1.0.0" + , "commit": "c29b671f798e82ba26b5f54ebc9e24c7dcfb8166" + , "subdir": "etc/defaults" + } + } + , "just/import targets": + { "repository": + { "type": "git" + , "repository": "https://github.com/just-buildsystem/justbuild" + , "branch": "v1.0.0" + , "commit": "c29b671f798e82ba26b5f54ebc9e24c7dcfb8166" + , "subdir": "etc/import" + } + } + } +} diff --git a/etc/repos.template.json b/etc/repos.template.json new file mode 100644 index 0000000..1b54fe9 --- /dev/null +++ b/etc/repos.template.json @@ -0,0 +1,19 @@ +{ "main": "rules" +, "repositories": + { "rules": + { "repository": {"type": "file", "path": "rules"} + , "bindings": + {"protoc": "just/protobuf", "grpc": "just/com_github_grpc_grpc"} + } + , "tests": + { "repository": {"type": "file", "path": "tests"} + , "bindings": {"test-just": "just", "test-rules": "test-rules"} + } + , "imports": {"repository": {"type": "file", "path": "etc/imports"}} + , "test-rules": + { "repository": {"type": "file", "path": "rules"} + , "target_root": "imports" + , "target_file_name": "rules.TARGETS" + } + } +} diff --git a/tests/ROOT b/tests/ROOT new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/tests/ROOT diff --git a/tests/TARGETS b/tests/TARGETS new file mode 100644 index 0000000..c64245e --- /dev/null +++ b/tests/TARGETS @@ -0,0 +1,6 @@ +{ "ALL": + { "type": "install" + , "deps": [["test_cases/deps", "ALL"], ["test_cases/cflags", "ALL"]] + , "tainted": ["test"] + } +} diff --git a/tests/test_cases/cflags/TARGETS b/tests/test_cases/cflags/TARGETS new file mode 100644 index 0000000..44c99f1 --- /dev/null +++ b/tests/test_cases/cflags/TARGETS @@ -0,0 +1,19 @@ +{ "public": + { "type": ["test_rules", "test_case"] + , "name": ["cflags_public"] + , "targets": + [ "+main_use_half3" + , "+test_use_half3" + , "+main_use_half3f" + , "+test_use_half3f" + ] + , "asserts": + [ "./main_use_half3/main_use_half3 | grep 1.5" + , "[ \"$(cat ./test_use_half3/result)\" = \"PASS\" ]" + , "./main_use_half3f/main_use_half3f | grep 1.5" + , "[ \"$(cat ./test_use_half3f/result)\" = \"PASS\" ]" + ] + , "data": [["TREE", null, "public"]] + } +, "ALL": {"type": "install", "deps": ["public"], "tainted": ["test"]} +} diff --git a/tests/test_cases/cflags/public/TARGETS b/tests/test_cases/cflags/public/TARGETS new file mode 100644 index 0000000..79aadb3 --- /dev/null +++ b/tests/test_cases/cflags/public/TARGETS @@ -0,0 +1,49 @@ +{ "half": + { "type": ["@", "rules", "CC", "library"] + , "name": ["half"] + , "hdrs": ["half.hpp"] + , "srcs": ["half.cpp"] + , "cflags": ["-DHALF_PRECISION_DOUBLE"] + , "stage": ["half"] + } +, "half3": + { "type": ["@", "rules", "CC", "library"] + , "name": ["half3"] + , "hdrs": ["half3.hpp"] + , "srcs": ["half3.cpp"] + , "deps": ["half"] + , "stage": ["half3"] + } +, "half3f": + { "type": ["@", "rules", "CC", "library"] + , "name": ["half3f"] + , "hdrs": ["half3f.hpp"] + , "srcs": ["half3f.cpp"] + , "private-deps": ["half3"] + , "stage": ["half3f"] + } +, "main_use_half3": + { "type": ["@", "rules", "CC", "binary"] + , "name": ["main_use_half3"] + , "srcs": ["main_use_half3.cpp"] + , "private-deps": ["half3"] + } +, "test_use_half3": + { "type": ["@", "rules", "CC/test", "test"] + , "name": ["test_use_half3"] + , "srcs": ["test_use_half3.cpp"] + , "private-deps": ["half3"] + } +, "main_use_half3f": + { "type": ["@", "rules", "CC", "binary"] + , "name": ["main_use_half3f"] + , "srcs": ["main_use_half3f.cpp"] + , "private-deps": ["half3f"] + } +, "test_use_half3f": + { "type": ["@", "rules", "CC/test", "test"] + , "name": ["test_use_half3f"] + , "srcs": ["test_use_half3f.cpp"] + , "private-deps": ["half3f"] + } +} diff --git a/tests/test_cases/cflags/public/half.cpp b/tests/test_cases/cflags/public/half.cpp new file mode 100644 index 0000000..e26ff6e --- /dev/null +++ b/tests/test_cases/cflags/public/half.cpp @@ -0,0 +1,5 @@ +#include "half.hpp" + +HALF_RESULT_TYPE half(int val) { + return static_cast<HALF_RESULT_TYPE>(val / 2.0); +} diff --git a/tests/test_cases/cflags/public/half.hpp b/tests/test_cases/cflags/public/half.hpp new file mode 100644 index 0000000..0baebcb --- /dev/null +++ b/tests/test_cases/cflags/public/half.hpp @@ -0,0 +1,12 @@ +#ifndef HALF_HPP +#define HALF_HPP + +#ifdef HALF_PRECISION_DOUBLE +#define HALF_RESULT_TYPE double +#else +#define HALF_RESULT_TYPE int +#endif + +HALF_RESULT_TYPE half(int val); + +#endif diff --git a/tests/test_cases/cflags/public/half3.cpp b/tests/test_cases/cflags/public/half3.cpp new file mode 100644 index 0000000..484f7f8 --- /dev/null +++ b/tests/test_cases/cflags/public/half3.cpp @@ -0,0 +1,3 @@ +#include "half3.hpp" + +HALF_RESULT_TYPE half3() { return half(3); } diff --git a/tests/test_cases/cflags/public/half3.hpp b/tests/test_cases/cflags/public/half3.hpp new file mode 100644 index 0000000..271ed68 --- /dev/null +++ b/tests/test_cases/cflags/public/half3.hpp @@ -0,0 +1,8 @@ +#ifndef HALF3_HPP +#define HALF3_HPP + +#include "half/half.hpp" + +HALF_RESULT_TYPE half3(); + +#endif diff --git a/tests/test_cases/cflags/public/half3f.cpp b/tests/test_cases/cflags/public/half3f.cpp new file mode 100644 index 0000000..88b6d55 --- /dev/null +++ b/tests/test_cases/cflags/public/half3f.cpp @@ -0,0 +1,7 @@ +#include "half3/half3.hpp" + +#ifndef HALF_PRECISION_DOUBLE +#error should be defined +#endif + +float half3f() { return static_cast<float>(half3()); } diff --git a/tests/test_cases/cflags/public/half3f.hpp b/tests/test_cases/cflags/public/half3f.hpp new file mode 100644 index 0000000..1207f63 --- /dev/null +++ b/tests/test_cases/cflags/public/half3f.hpp @@ -0,0 +1,6 @@ +#ifndef BAZ_HPP +#define BAZ_HPP + +float half3f(); + +#endif diff --git a/tests/test_cases/cflags/public/main_use_half3.cpp b/tests/test_cases/cflags/public/main_use_half3.cpp new file mode 100644 index 0000000..1c9d6ba --- /dev/null +++ b/tests/test_cases/cflags/public/main_use_half3.cpp @@ -0,0 +1,7 @@ +#include "half3/half3.hpp" +#include <iostream> + +int main() { + std::cout << half3() << std::endl; + return 0; +} diff --git a/tests/test_cases/cflags/public/main_use_half3f.cpp b/tests/test_cases/cflags/public/main_use_half3f.cpp new file mode 100644 index 0000000..3e855cd --- /dev/null +++ b/tests/test_cases/cflags/public/main_use_half3f.cpp @@ -0,0 +1,11 @@ +#include "half3f/half3f.hpp" +#include <iostream> + +#ifdef HALF_PRECISION_DOUBLE +#error should not be defined +#endif + +int main() { + std::cout << half3f() << std::endl; + return 0; +} diff --git a/tests/test_cases/cflags/public/test_use_half3.cpp b/tests/test_cases/cflags/public/test_use_half3.cpp new file mode 100644 index 0000000..1cf50ad --- /dev/null +++ b/tests/test_cases/cflags/public/test_use_half3.cpp @@ -0,0 +1,6 @@ +#include "half3/half3.hpp" + +int main() { + auto result = half3(); + return result > 1 and result < 2 ? 0 : 1; +} diff --git a/tests/test_cases/cflags/public/test_use_half3f.cpp b/tests/test_cases/cflags/public/test_use_half3f.cpp new file mode 100644 index 0000000..078e69c --- /dev/null +++ b/tests/test_cases/cflags/public/test_use_half3f.cpp @@ -0,0 +1,10 @@ +#include "half3f/half3f.hpp" + +#ifdef HALF_PRECISION_DOUBLE +#error should not be defined +#endif + +int main() { + auto result = half3f(); + return result > 1 and result < 2 ? 0 : 1; +} diff --git a/tests/test_cases/deps/TARGETS b/tests/test_cases/deps/TARGETS new file mode 100644 index 0000000..3fcf850 --- /dev/null +++ b/tests/test_cases/deps/TARGETS @@ -0,0 +1,34 @@ +{ "private": + { "type": ["test_rules", "test_case"] + , "name": ["deps_private"] + , "targets": + ["+foo", "-main_includes_foo", "+main_links_foo", "+main_links_bar_foo"] + , "asserts": + [ "test -f foo/foo/libfoo.a" + , "test -f foo/foo/foo.hpp" + , "! test -f foo/bar/bar.hpp" + , "./main_links_foo/main | grep foo" + , "./main_links_bar_foo/main | grep bar" + , "./main_links_bar_foo/main | grep foo" + ] + , "data": [["TREE", null, "private"]] + } +, "public": + { "type": ["test_rules", "test_case"] + , "name": ["deps_public"] + , "targets": + ["+foo", "+main_includes_foo", "+main_links_foo", "+main_links_bar_foo"] + , "asserts": + [ "test -f foo/foo/libfoo.a" + , "test -f foo/foo/foo.hpp" + , "! test -f foo/bar/bar.hpp" + , "./main_includes_foo/main | grep main" + , "./main_links_foo/main | grep foo" + , "./main_links_bar_foo/main | grep bar" + , "./main_links_bar_foo/main | grep foo" + ] + , "data": [["TREE", null, "public"]] + } +, "ALL": + {"type": "install", "deps": ["private", "public"], "tainted": ["test"]} +} diff --git a/tests/test_cases/deps/private/TARGETS b/tests/test_cases/deps/private/TARGETS new file mode 100644 index 0000000..582af60 --- /dev/null +++ b/tests/test_cases/deps/private/TARGETS @@ -0,0 +1,34 @@ +{ "foo": + { "type": ["@", "rules", "CC", "library"] + , "name": ["foo"] + , "hdrs": ["foo.hpp"] + , "srcs": ["foo.cpp"] + , "stage": ["foo"] + } +, "bar": + { "type": ["@", "rules", "CC", "library"] + , "name": ["bar"] + , "hdrs": ["bar.hpp"] + , "srcs": ["bar.cpp"] + , "private-deps": ["foo"] + , "stage": ["bar"] + } +, "main_includes_foo": + { "type": ["@", "rules", "CC", "binary"] + , "name": ["main"] + , "srcs": ["main_includes_foo.cpp"] + , "private-deps": ["bar"] + } +, "main_links_foo": + { "type": ["@", "rules", "CC", "binary"] + , "name": ["main"] + , "srcs": ["main_links_foo.cpp"] + , "private-deps": ["bar"] + } +, "main_links_bar_foo": + { "type": ["@", "rules", "CC", "binary"] + , "name": ["main"] + , "srcs": ["main_links_bar_foo.cpp"] + , "private-deps": ["bar"] + } +} diff --git a/tests/test_cases/deps/private/bar.cpp b/tests/test_cases/deps/private/bar.cpp new file mode 100644 index 0000000..dfce0e8 --- /dev/null +++ b/tests/test_cases/deps/private/bar.cpp @@ -0,0 +1,8 @@ +#include "foo/foo.hpp" +#include "bar.hpp" +#include <iostream> + +int bar() { + std::cout << "bar\n"; + return foo(); +} diff --git a/tests/test_cases/deps/private/bar.hpp b/tests/test_cases/deps/private/bar.hpp new file mode 100644 index 0000000..94d4f16 --- /dev/null +++ b/tests/test_cases/deps/private/bar.hpp @@ -0,0 +1,6 @@ +#ifndef BAR_HPP +#define BAR_HPP + +int bar(); + +#endif diff --git a/tests/test_cases/deps/private/foo.cpp b/tests/test_cases/deps/private/foo.cpp new file mode 100644 index 0000000..f985022 --- /dev/null +++ b/tests/test_cases/deps/private/foo.cpp @@ -0,0 +1,7 @@ +#include "foo.hpp" +#include <iostream> + +int foo() { + std::cout << "foo\n"; + return 0; +} diff --git a/tests/test_cases/deps/private/foo.hpp b/tests/test_cases/deps/private/foo.hpp new file mode 100644 index 0000000..1a28686 --- /dev/null +++ b/tests/test_cases/deps/private/foo.hpp @@ -0,0 +1,6 @@ +#ifndef FOO_HPP +#define FOO_HPP + +int foo(); + +#endif diff --git a/tests/test_cases/deps/private/main_includes_foo.cpp b/tests/test_cases/deps/private/main_includes_foo.cpp new file mode 100644 index 0000000..16701c7 --- /dev/null +++ b/tests/test_cases/deps/private/main_includes_foo.cpp @@ -0,0 +1,9 @@ +// test that foo.hpp not available + +#include "foo/foo.hpp" +#include <iostream> + +int main() { + std::cout << "main\n"; + return 0; +} diff --git a/tests/test_cases/deps/private/main_links_bar_foo.cpp b/tests/test_cases/deps/private/main_links_bar_foo.cpp new file mode 100644 index 0000000..e936074 --- /dev/null +++ b/tests/test_cases/deps/private/main_links_bar_foo.cpp @@ -0,0 +1,9 @@ +// test that foo is linked after bar + +#include "bar/bar.hpp" +#include <iostream> + +int main() { + std::cout << "main\n"; + return bar(); +} diff --git a/tests/test_cases/deps/private/main_links_foo.cpp b/tests/test_cases/deps/private/main_links_foo.cpp new file mode 100644 index 0000000..1e1e07a --- /dev/null +++ b/tests/test_cases/deps/private/main_links_foo.cpp @@ -0,0 +1,10 @@ +// test that foo is linked + +#include <iostream> + +int foo(); // forward declare + +int main() { + std::cout << "main\n"; + return foo(); +} diff --git a/tests/test_cases/deps/public/TARGETS b/tests/test_cases/deps/public/TARGETS new file mode 100644 index 0000000..504333e --- /dev/null +++ b/tests/test_cases/deps/public/TARGETS @@ -0,0 +1,34 @@ +{ "foo": + { "type": ["@", "rules", "CC", "library"] + , "name": ["foo"] + , "hdrs": ["foo.hpp"] + , "srcs": ["foo.cpp"] + , "stage": ["foo"] + } +, "bar": + { "type": ["@", "rules", "CC", "library"] + , "name": ["bar"] + , "hdrs": ["bar.hpp"] + , "srcs": ["bar.cpp"] + , "deps": ["foo"] + , "stage": ["bar"] + } +, "main_includes_foo": + { "type": ["@", "rules", "CC", "binary"] + , "name": ["main"] + , "srcs": ["main_includes_foo.cpp"] + , "private-deps": ["bar"] + } +, "main_links_foo": + { "type": ["@", "rules", "CC", "binary"] + , "name": ["main"] + , "srcs": ["main_links_foo.cpp"] + , "private-deps": ["bar"] + } +, "main_links_bar_foo": + { "type": ["@", "rules", "CC", "binary"] + , "name": ["main"] + , "srcs": ["main_links_bar_foo.cpp"] + , "private-deps": ["bar"] + } +} diff --git a/tests/test_cases/deps/public/bar.cpp b/tests/test_cases/deps/public/bar.cpp new file mode 100644 index 0000000..f2a779a --- /dev/null +++ b/tests/test_cases/deps/public/bar.cpp @@ -0,0 +1,7 @@ +#include "bar.hpp" +#include <iostream> + +int bar(Foo *foo) { + std::cout << "bar\n"; + return (foo == nullptr) ? -1 : foo->foo(); +} diff --git a/tests/test_cases/deps/public/bar.hpp b/tests/test_cases/deps/public/bar.hpp new file mode 100644 index 0000000..ec9c165 --- /dev/null +++ b/tests/test_cases/deps/public/bar.hpp @@ -0,0 +1,8 @@ +#ifndef BAR_HPP +#define BAR_HPP + +#include "foo/foo.hpp" + +int bar(Foo *foo); + +#endif diff --git a/tests/test_cases/deps/public/foo.cpp b/tests/test_cases/deps/public/foo.cpp new file mode 100644 index 0000000..6899c7a --- /dev/null +++ b/tests/test_cases/deps/public/foo.cpp @@ -0,0 +1,7 @@ +#include "foo.hpp" +#include <iostream> + +int Foo::foo() { + std::cout << "foo\n"; + return 0; +} diff --git a/tests/test_cases/deps/public/foo.hpp b/tests/test_cases/deps/public/foo.hpp new file mode 100644 index 0000000..025f3ef --- /dev/null +++ b/tests/test_cases/deps/public/foo.hpp @@ -0,0 +1,8 @@ +#ifndef FOO_HPP +#define FOO_HPP + +struct Foo { + int foo(); +}; + +#endif diff --git a/tests/test_cases/deps/public/main_includes_foo.cpp b/tests/test_cases/deps/public/main_includes_foo.cpp new file mode 100644 index 0000000..e8a85b6 --- /dev/null +++ b/tests/test_cases/deps/public/main_includes_foo.cpp @@ -0,0 +1,9 @@ +// test that foo.hpp is available (despite unused here) + +#include "foo/foo.hpp" +#include <iostream> + +int main() { + std::cout << "main\n"; + return 0; +} diff --git a/tests/test_cases/deps/public/main_links_bar_foo.cpp b/tests/test_cases/deps/public/main_links_bar_foo.cpp new file mode 100644 index 0000000..02c2966 --- /dev/null +++ b/tests/test_cases/deps/public/main_links_bar_foo.cpp @@ -0,0 +1,10 @@ +// test that foo is linked after bar + +#include "bar/bar.hpp" +#include <iostream> + +int main() { + std::cout << "main\n"; + Foo foo{}; + return bar(&foo); +} diff --git a/tests/test_cases/deps/public/main_links_foo.cpp b/tests/test_cases/deps/public/main_links_foo.cpp new file mode 100644 index 0000000..f9cc308 --- /dev/null +++ b/tests/test_cases/deps/public/main_links_foo.cpp @@ -0,0 +1,10 @@ +// test that foo is linked + +#include "foo/foo.hpp" +#include <iostream> + +int main() { + std::cout << "main\n"; + Foo{}.foo(); + return 0; +} diff --git a/tests/test_rules/EXPRESSIONS b/tests/test_rules/EXPRESSIONS new file mode 100644 index 0000000..f1ca6f4 --- /dev/null +++ b/tests/test_rules/EXPRESSIONS @@ -0,0 +1,46 @@ +{ "stage_singleton_field": + { "vars": ["fieldname", "transition", "location"] + , "expression": + { "type": "assert_non_empty" + , "msg": + ["No artifact specified in field", {"type": "var", "name": "fieldname"}] + , "$1": + { "type": "disjoint_map_union" + , "msg": + [ "Expecting (essentially) a single artifact in field" + , {"type": "var", "name": "fieldname"} + ] + , "$1": + { "type": "foreach" + , "var": "src" + , "range": + {"type": "FIELD", "name": {"type": "var", "name": "fieldname"}} + , "body": + { "type": "disjoint_map_union" + , "$1": + { "type": "foreach" + , "var": "artifact" + , "range": + { "type": "values" + , "$1": + { "type": "DEP_ARTIFACTS" + , "dep": {"type": "var", "name": "src"} + , "transition": + { "type": "var" + , "name": "transition" + , "default": {"type": "empty_map"} + } + } + } + , "body": + { "type": "singleton_map" + , "key": {"type": "var", "name": "location"} + , "value": {"type": "var", "name": "artifact"} + } + } + } + } + } + } + } +} diff --git a/tests/test_rules/README.md b/tests/test_rules/README.md new file mode 100644 index 0000000..8ed8f91 --- /dev/null +++ b/tests/test_rules/README.md @@ -0,0 +1,25 @@ +# Test Rules + +This is a test rule that supports building and installing multiple targets with +a given set of rules. For each target, it can be specified whether building it +should fail or succeed. After processing all targets, additional assertions +(list of shell commands) can be run. + +## Setup + +The test rules expect to find the following two bindings: + - `[["@", "test-rules", "", "tree"]]`, which contains a single tree artifact + with the rules to test. + - `[["@", "test-just", "", ""]]`, which contains a single executable artifact + that is the JustBuild binary to use for the tests. + +## Rule `["test_rules", "test_case"]` + +Define a test case for rule tests. + +| Field | Description | +| ----- | ----------- | +| `"name"` | Name of the test (multiple entries are joined). | +| `"targets"` | Target names to build and install. Each target name is prefixed by `"+"` or `"-"`, indicating if the build should fail or not. Targets that build successfully will be installed to a directory named identical to the target name (without the prefix). | +| `"asserts"` | List of commands to execute after all targets were processed. To access artifacts from installed targets, use the corresponding target name as prefix dir (e.g., target `"+foo"` installs to `"./foo/"`). | +| `"data"` | The directory that contains the project with the targets to test. | diff --git a/tests/test_rules/RULES b/tests/test_rules/RULES new file mode 100644 index 0000000..1a65ec8 --- /dev/null +++ b/tests/test_rules/RULES @@ -0,0 +1,121 @@ +{ "test_case": + { "doc": ["Define a test case for rule tests."] + , "string_fields": ["name", "targets", "asserts"] + , "target_fields": ["data"] + , "field_doc": + { "name": ["Name of the test (multiple entries are joined)."] + , "targets": + [ "Target names to build and install. Each target name is prefixed by" + , "\"+\" or \"-\", indicating if the build should fail or not." + , "Targets that build successfully will be installed to a directory" + , "named identical to the target name (without the prefix)." + ] + , "asserts": + [ "List of commands to execute after all targets were processed. To" + , "access artifacts from installed targets, use the corresponding target" + , "name as prefix dir (e.g., target \"+foo\" installs to \"./foo/\")." + ] + , "data": + ["The directory that contains the project with the targets to test."] + } + , "tainted": ["test"] + , "implicit": + { "runner": ["test_runner.py"] + , "rules": [["@", "test-rules", "", "tree"]] + , "just": [["@", "test-just", "", ""]] + } + , "imports": {"stage_artifact": "stage_singleton_field"} + , "expression": + { "type": "let*" + , "bindings": + [ ["name", {"type": "join", "$1": {"type": "FIELD", "name": "name"}}] + , ["fieldname", "just"] + , ["location", "bin/just"] + , ["just", {"type": "CALL_EXPRESSION", "name": "stage_artifact"}] + , ["fieldname", "rules"] + , ["location", "rules"] + , ["rules", {"type": "CALL_EXPRESSION", "name": "stage_artifact"}] + , ["fieldname", "data"] + , ["location", "work"] + , ["work", {"type": "CALL_EXPRESSION", "name": "stage_artifact"}] + , ["fieldname", "runner"] + , ["location", "runner"] + , ["runner", {"type": "CALL_EXPRESSION", "name": "stage_artifact"}] + , ["targets", {"type": "FIELD", "name": "targets"}] + , ["asserts", {"type": "FIELD", "name": "asserts"}] + , [ "repos" + , { "type": "singleton_map" + , "key": "repos.json" + , "value": + { "type": "BLOB" + , "data": + { "type": "json_encode" + , "$1": + { "type": "let*" + , "bindings": + [ ["workspace_root", ["file", "work"]] + , ["rules", "rules"] + , ["bindings", {"type": "env", "vars": ["rules"]}] + , [ "work" + , {"type": "env", "vars": ["workspace_root", "bindings"]} + ] + , ["workspace_root", ["file", "rules"]] + , ["rules", {"type": "env", "vars": ["workspace_root"]}] + , [ "repositories" + , {"type": "env", "vars": ["rules", "work"]} + ] + , ["main", "work"] + ] + , "body": {"type": "env", "vars": ["main", "repositories"]} + } + } + } + } + ] + , [ "config" + , { "type": "singleton_map" + , "key": "config.json" + , "value": + { "type": "BLOB" + , "data": + { "type": "json_encode" + , "$1": {"type": "env", "vars": ["targets", "asserts"]} + } + } + } + ] + , [ "results" + , { "type": "ACTION" + , "inputs": + { "type": "map_union" + , "$1": + [ {"type": "var", "name": "runner"} + , {"type": "var", "name": "rules"} + , {"type": "var", "name": "just"} + , {"type": "var", "name": "repos"} + , {"type": "var", "name": "work"} + , {"type": "var", "name": "config"} + ] + } + , "outs": ["stdout", "stderr", "result", "time-start", "time-stop"] + , "cmd": ["./runner"] + , "may_fail": ["test"] + , "fail_message": + { "type": "join" + , "$1": ["Rule test ", {"type": "var", "name": "name"}, " failed"] + } + } + ] + ] + , "body": + { "type": "RESULT" + , "artifacts": {"type": "var", "name": "results"} + , "runfiles": + { "type": "singleton_map" + , "key": {"type": "var", "name": "name"} + , "value": {"type": "TREE", "$1": {"type": "var", "name": "results"}} + } + } + } + } +} diff --git a/tests/test_rules/TARGETS b/tests/test_rules/TARGETS new file mode 100644 index 0000000..0967ef4 --- /dev/null +++ b/tests/test_rules/TARGETS @@ -0,0 +1 @@ +{} diff --git a/tests/test_rules/test_runner.py b/tests/test_rules/test_runner.py new file mode 100755 index 0000000..976f041 --- /dev/null +++ b/tests/test_rules/test_runner.py @@ -0,0 +1,89 @@ +#!/usr/bin/env python3 +# Copyright 2022 Huawei Cloud Computing Technology Co., Ltd. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import json +import os +import subprocess +import time + +time_start = time.time() +time_stop = 0 +result = "UNKNOWN" +stderr = "" +stdout = "" + + +def dump_results(): + with open("result", "w") as f: + f.write("%s\n" % (result, )) + with open("time-start", "w") as f: + f.write("%d\n" % (time_start, )) + with open("time-stop", "w") as f: + f.write("%d\n" % (time_stop, )) + with open("stdout", "w") as f: + f.write("%s\n" % (stdout, )) + with open("stderr", "w") as f: + f.write("%s\n" % (stderr, )) + + +dump_results() + +with open('config.json') as f: + config = json.load(f) + +os.makedirs("./outs") + +# install targets +failed_targets = 0 +stdout += "Test targets:\n" +for t in config.get('targets', []): + target = t[1:] + should_fail = t[0] == "-" + ret = subprocess.run([ + "./bin/just", "install", "--local-build-root", "./build_root", "-C", + "repos.json", "-o", "/".join(["./outs", target]), target + ], + capture_output=True) + success = ret.returncode != 0 if should_fail else ret.returncode == 0 + failed_targets += 0 if success else 1 + stdout += f" [{'PASS' if success else 'FAIL'}] {target}\n" + stderr += "".join([ + f"stdout/stderr of test target '{target}':\n", + ret.stdout.decode("utf-8"), + ret.stderr.decode("utf-8"), "\n" + ]) +stdout += f" {failed_targets} targets failed\n" + +# run asserts +failed_asserts = 0 +stdout += "Test asserts:\n" +for cmd in config.get('asserts', []): + ret = subprocess.run(cmd, cwd="./outs", shell=True, capture_output=True) + success = ret.returncode == 0 + failed_asserts += 0 if success else 1 + stdout += f" [{'PASS' if success else 'FAIL'}] {cmd}\n" + stderr += "".join([ + f"stdout/stderr of test assert '{cmd}':\n", + ret.stdout.decode("utf-8"), + ret.stderr.decode("utf-8"), "\n" + ]) +stdout += f" {failed_asserts} asserts failed\n" + +retval = min(failed_targets + failed_asserts, 125) +result = "PASS" if retval == 0 else "FAIL" + +time_stop = time.time() +dump_results() +exit(retval) |