summaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
Diffstat (limited to 'tests')
-rw-r--r--tests/ROOT0
-rw-r--r--tests/TARGETS10
-rw-r--r--tests/test_cases/cflags/TARGETS19
-rw-r--r--tests/test_cases/cflags/public/TARGETS49
-rw-r--r--tests/test_cases/cflags/public/half.cpp5
-rw-r--r--tests/test_cases/cflags/public/half.hpp12
-rw-r--r--tests/test_cases/cflags/public/half3.cpp3
-rw-r--r--tests/test_cases/cflags/public/half3.hpp8
-rw-r--r--tests/test_cases/cflags/public/half3f.cpp7
-rw-r--r--tests/test_cases/cflags/public/half3f.hpp6
-rw-r--r--tests/test_cases/cflags/public/main_use_half3.cpp7
-rw-r--r--tests/test_cases/cflags/public/main_use_half3f.cpp11
-rw-r--r--tests/test_cases/cflags/public/test_use_half3.cpp6
-rw-r--r--tests/test_cases/cflags/public/test_use_half3f.cpp10
-rw-r--r--tests/test_cases/config/TARGETS57
-rw-r--r--tests/test_cases/config/data/TARGETS48
-rw-r--r--tests/test_cases/config/data/foo.in13
-rwxr-xr-xtests/test_cases/config/run_pytest.sh3
-rw-r--r--tests/test_cases/config/runner_test.py134
-rw-r--r--tests/test_cases/deps/TARGETS419
-rw-r--r--tests/test_cases/deps/chain/TARGETS38
-rw-r--r--tests/test_cases/deps/chain/bar.cpp5
-rw-r--r--tests/test_cases/deps/chain/bar.hpp8
-rw-r--r--tests/test_cases/deps/chain/baz.cpp5
-rw-r--r--tests/test_cases/deps/chain/baz.hpp8
-rw-r--r--tests/test_cases/deps/chain/foo.cpp5
-rw-r--r--tests/test_cases/deps/chain/foo.hpp8
-rw-r--r--tests/test_cases/deps/chain/main.cpp7
-rw-r--r--tests/test_cases/deps/chain/qux.cpp5
-rw-r--r--tests/test_cases/deps/chain/qux.hpp8
-rw-r--r--tests/test_cases/deps/cmake/TARGETS76
-rw-r--r--tests/test_cases/deps/cmake/main.cpp8
-rw-r--r--tests/test_cases/deps/cmake/test.cpp3
-rw-r--r--tests/test_cases/deps/cmake/test.sh5
-rw-r--r--tests/test_cases/deps/components/TARGETS55
-rw-r--r--tests/test_cases/deps/components/bar.cpp9
-rw-r--r--tests/test_cases/deps/components/bar.hpp6
-rw-r--r--tests/test_cases/deps/components/foo.cpp10
-rw-r--r--tests/test_cases/deps/components/foo.hpp8
-rw-r--r--tests/test_cases/deps/components/foodep.cpp9
-rw-r--r--tests/test_cases/deps/components/foodep.hpp8
-rw-r--r--tests/test_cases/deps/components/main.cpp10
-rw-r--r--tests/test_cases/deps/install/TARGETS69
-rw-r--r--tests/test_cases/deps/install/bar.cpp7
-rw-r--r--tests/test_cases/deps/install/bar.hpp8
-rw-r--r--tests/test_cases/deps/install/baz.cpp7
-rw-r--r--tests/test_cases/deps/install/baz.hpp10
-rw-r--r--tests/test_cases/deps/install/foo.cpp10
-rw-r--r--tests/test_cases/deps/install/foo.hpp8
-rw-r--r--tests/test_cases/deps/install/main.cpp10
-rw-r--r--tests/test_cases/deps/install/qux.hpp8
-rw-r--r--tests/test_cases/deps/lint/TARGETS78
-rw-r--r--tests/test_cases/deps/lint/bar.cpp10
-rw-r--r--tests/test_cases/deps/lint/bar.hpp8
-rw-r--r--tests/test_cases/deps/lint/bardep.cpp9
-rw-r--r--tests/test_cases/deps/lint/bardep.hpp8
-rw-r--r--tests/test_cases/deps/lint/check-main.sh3
-rwxr-xr-xtests/test_cases/deps/lint/expect.py24
-rw-r--r--tests/test_cases/deps/lint/foo.cpp10
-rw-r--r--tests/test_cases/deps/lint/foo.hpp8
-rw-r--r--tests/test_cases/deps/lint/foodep.cpp9
-rw-r--r--tests/test_cases/deps/lint/foodep.hpp8
-rw-r--r--tests/test_cases/deps/lint/main.cpp9
-rw-r--r--tests/test_cases/deps/lint/plain.cpp5
-rw-r--r--tests/test_cases/deps/lint/plain.hpp9
-rwxr-xr-xtests/test_cases/deps/lint/pretend_lint.py15
-rwxr-xr-xtests/test_cases/deps/lint/summary.py27
-rw-r--r--tests/test_cases/deps/object/TARGETS49
-rw-r--r--tests/test_cases/deps/object/bar.cpp3
-rw-r--r--tests/test_cases/deps/object/bar.hpp9
-rw-r--r--tests/test_cases/deps/object/foo.cpp3
-rw-r--r--tests/test_cases/deps/object/foo.hpp8
-rw-r--r--tests/test_cases/deps/object/main.cpp8
-rw-r--r--tests/test_cases/deps/prebuilt/TARGETS158
-rw-r--r--tests/test_cases/deps/prebuilt/bar.hpp8
-rw-r--r--tests/test_cases/deps/prebuilt/foo.hpp8
-rw-r--r--tests/test_cases/deps/private/TARGETS36
-rw-r--r--tests/test_cases/deps/private/bar.cpp8
-rw-r--r--tests/test_cases/deps/private/bar.hpp6
-rw-r--r--tests/test_cases/deps/private/foo.cpp7
-rw-r--r--tests/test_cases/deps/private/foo.hpp6
-rw-r--r--tests/test_cases/deps/private/main_includes_foo.cpp9
-rw-r--r--tests/test_cases/deps/private/main_links_bar_foo.cpp9
-rw-r--r--tests/test_cases/deps/private/main_links_foo.cpp10
-rw-r--r--tests/test_cases/deps/public/TARGETS36
-rw-r--r--tests/test_cases/deps/public/bar.cpp7
-rw-r--r--tests/test_cases/deps/public/bar.hpp8
-rw-r--r--tests/test_cases/deps/public/foo.cpp7
-rw-r--r--tests/test_cases/deps/public/foo.hpp8
-rw-r--r--tests/test_cases/deps/public/main_includes_foo.cpp9
-rw-r--r--tests/test_cases/deps/public/main_links_bar_foo.cpp10
-rw-r--r--tests/test_cases/deps/public/main_links_foo.cpp10
-rw-r--r--tests/test_cases/deps/shared/TARGETS104
-rw-r--r--tests/test_cases/deps/shared/bar.cpp8
-rw-r--r--tests/test_cases/deps/shared/bar.hpp6
-rw-r--r--tests/test_cases/deps/shared/baz.cpp8
-rw-r--r--tests/test_cases/deps/shared/baz.hpp6
-rw-r--r--tests/test_cases/deps/shared/foo.cpp7
-rw-r--r--tests/test_cases/deps/shared/foo.hpp6
-rw-r--r--tests/test_cases/deps/shared/main_uses_bar.cpp8
-rw-r--r--tests/test_cases/deps/shared/main_uses_foo.cpp8
-rw-r--r--tests/test_cases/deps/shared/test_diamond.cpp4
-rw-r--r--tests/test_cases/deps/shared/test_uses_bar.cpp3
-rw-r--r--tests/test_cases/deps/shared/test_uses_baz.cpp3
-rw-r--r--tests/test_cases/deps/shared/test_uses_foo.cpp3
-rw-r--r--tests/test_cases/deps/shared/test_uses_main.sh7
-rw-r--r--tests/test_cases/deps/transitive-components/TARGETS64
-rw-r--r--tests/test_cases/deps/transitive-components/bar.cpp10
-rw-r--r--tests/test_cases/deps/transitive-components/bar.hpp8
-rw-r--r--tests/test_cases/deps/transitive-components/bardep.cpp9
-rw-r--r--tests/test_cases/deps/transitive-components/bardep.hpp8
-rw-r--r--tests/test_cases/deps/transitive-components/baz.cpp10
-rw-r--r--tests/test_cases/deps/transitive-components/baz.hpp8
-rw-r--r--tests/test_cases/deps/transitive-components/bazdep.cpp9
-rw-r--r--tests/test_cases/deps/transitive-components/bazdep.hpp8
-rw-r--r--tests/test_cases/deps/transitive-components/foo.cpp10
-rw-r--r--tests/test_cases/deps/transitive-components/foo.hpp8
-rw-r--r--tests/test_cases/deps/transitive-components/foodep.cpp9
-rw-r--r--tests/test_cases/deps/transitive-components/foodep.hpp8
-rw-r--r--tests/test_cases/deps/transitive-components/main.cpp11
-rw-r--r--tests/test_rules/EXPRESSIONS46
-rw-r--r--tests/test_rules/README.md28
-rw-r--r--tests/test_rules/RULES243
-rw-r--r--tests/test_rules/TARGETS1
-rwxr-xr-xtests/test_rules/test_runner.py90
125 files changed, 2717 insertions, 0 deletions
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..e9b0f2a
--- /dev/null
+++ b/tests/TARGETS
@@ -0,0 +1,10 @@
+{ "ALL":
+ { "type": "install"
+ , "deps":
+ [ ["test_cases/deps", "ALL"]
+ , ["test_cases/cflags", "ALL"]
+ , ["test_cases/config", "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/config/TARGETS b/tests/test_cases/config/TARGETS
new file mode 100644
index 0000000..b751130
--- /dev/null
+++ b/tests/test_cases/config/TARGETS
@@ -0,0 +1,57 @@
+{ "both":
+ { "type": ["test_rules", "test_case"]
+ , "name": ["config_both"]
+ , "targets": ["+header_both"]
+ , "asserts":
+ [ "test -f header_both/foo.h"
+ , "cat header_both/foo.h"
+ , "grep '// comment 1$' header_both/foo.h"
+ , "grep '#define FOO 1$' header_both/foo.h"
+ , "grep '#define BAX 0$' header_both/foo.h"
+ , "grep '#define BAR \"foofoo bar foo\"$' header_both/foo.h"
+ , "grep '/\\* #undef BAZ \\*/$' header_both/foo.h"
+ , "grep '/\\* #undef BAX \\*/$' header_both/foo.h"
+ , "grep '#define FOO$' header_both/foo.h"
+ , "grep '// comment 2$' header_both/foo.h"
+ , "grep 'magic_at$' header_both/foo.h"
+ , "grep 'magic_curly$' header_both/foo.h"
+ , "grep 'undefined_at--$' header_both/foo.h"
+ , "grep 'undefined_curly--$' header_both/foo.h"
+ ]
+ , "data": [["TREE", null, "data"]]
+ }
+, "@only":
+ { "type": ["test_rules", "test_case"]
+ , "name": ["config_@only"]
+ , "targets": ["+header_@only"]
+ , "asserts":
+ [ "test -f header_@only/foo.h"
+ , "cat header_@only/foo.h"
+ , "grep '// comment 1$' header_@only/foo.h"
+ , "grep '#define FOO 1$' header_@only/foo.h"
+ , "grep '#define BAX 0$' header_@only/foo.h"
+ , "grep '#define BAR \"foofoo bar \\${FOO}\"$' header_@only/foo.h"
+ , "grep '/\\* #undef BAZ \\*/$' header_@only/foo.h"
+ , "grep '/\\* #undef BAX \\*/$' header_@only/foo.h"
+ , "grep '#define FOO$' header_@only/foo.h"
+ , "grep '// comment 2$' header_@only/foo.h"
+ , "grep 'magic_at$' header_@only/foo.h"
+ , "grep '\\${NO_MAGIC_CURLY}' header_@only/foo.h"
+ , "grep 'undefined_at--$' header_@only/foo.h"
+ , "grep 'undefined_curly-\\${UNDEFINED}-$' header_@only/foo.h"
+ ]
+ , "data": [["TREE", null, "data"]]
+ }
+, "runner.py":
+ { "type": "install"
+ , "files": {"runner.py": ["@", "rules", "CC/auto", "runner"]}
+ }
+, "pytest":
+ { "type": ["@", "rules", "shell/test", "script"]
+ , "name": ["config_pytest"]
+ , "test": ["run_pytest.sh"]
+ , "deps": ["runner.py", "runner_test.py"]
+ }
+, "ALL":
+ {"type": "install", "deps": ["both", "@only", "pytest"], "tainted": ["test"]}
+}
diff --git a/tests/test_cases/config/data/TARGETS b/tests/test_cases/config/data/TARGETS
new file mode 100644
index 0000000..f3e7cf0
--- /dev/null
+++ b/tests/test_cases/config/data/TARGETS
@@ -0,0 +1,48 @@
+{ "blueprint":
+ { "type": ["@", "rules", "CC/auto", "config_file"]
+ , "input": ["foo.in"]
+ , "output": ["foo.h"]
+ , "magic_string": ["cmakedefine"]
+ }
+, "blueprint_@only":
+ { "type": ["@", "rules", "CC/auto", "config_file"]
+ , "input": ["foo.in"]
+ , "output": ["foo.h"]
+ , "magic_string": ["cmakedefine"]
+ , "@only": ["true"]
+ }
+, "header_both":
+ { "type": "configure"
+ , "target": "blueprint"
+ , "config":
+ { "type": "let*"
+ , "bindings":
+ [ [ "defines"
+ , [ ["FOO", "foo"]
+ , ["BAR", "bar"]
+ , ["NO_MAGIC_AT", "magic_at"]
+ , ["NO_MAGIC_CURLY", "magic_curly"]
+ ]
+ ]
+ ]
+ , "body": {"type": "env", "vars": ["defines"]}
+ }
+ }
+, "header_@only":
+ { "type": "configure"
+ , "target": "blueprint_@only"
+ , "config":
+ { "type": "let*"
+ , "bindings":
+ [ [ "defines"
+ , [ ["FOO", "foo"]
+ , ["BAR", "bar"]
+ , ["NO_MAGIC_AT", "magic_at"]
+ , ["NO_MAGIC_CURLY", "magic_curly"]
+ ]
+ ]
+ ]
+ , "body": {"type": "env", "vars": ["defines"]}
+ }
+ }
+}
diff --git a/tests/test_cases/config/data/foo.in b/tests/test_cases/config/data/foo.in
new file mode 100644
index 0000000..66bd9ba
--- /dev/null
+++ b/tests/test_cases/config/data/foo.in
@@ -0,0 +1,13 @@
+// comment 1
+#cmakedefine01 FOO
+#cmakedefine01 BAX
+#cmakedefine FOO "@FOO@"
+#cmakedefine BAR "@FOO@@FOO@ bar ${FOO}"
+#cmakedefine BAZ "baz"
+#cmakedefine BAX
+#cmakedefine FOO
+// comment 2
+@NO_MAGIC_AT@
+${NO_MAGIC_CURLY}
+undefined_at-@UNDEFINED@-
+undefined_curly-${UNDEFINED}-
diff --git a/tests/test_cases/config/run_pytest.sh b/tests/test_cases/config/run_pytest.sh
new file mode 100755
index 0000000..fc886ea
--- /dev/null
+++ b/tests/test_cases/config/run_pytest.sh
@@ -0,0 +1,3 @@
+#!/bin/sh
+
+python3 -m pytest
diff --git a/tests/test_cases/config/runner_test.py b/tests/test_cases/config/runner_test.py
new file mode 100644
index 0000000..2f0920e
--- /dev/null
+++ b/tests/test_cases/config/runner_test.py
@@ -0,0 +1,134 @@
+# Copyright 2024 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.
+
+from runner import (
+ compute_value,
+ get_tokens,
+ handle01,
+ replace_value,
+ undefine,
+)
+
+from hypothesis import given
+from hypothesis.strategies import text
+import re
+
+
+def test_get_tokens():
+ magic_string = "cmakedefine"
+ match, is_01 = get_tokens(
+ f"# \t \t {magic_string} \t TOKEN_KEY \t\t TOKEN_VALUE \t\n", magic_string
+ )
+ assert match
+ assert not is_01
+ groups = match.groups()
+ assert len(groups) == 6
+ assert groups[1] == magic_string
+ assert groups[3] == "TOKEN_KEY"
+ assert groups[5] == "TOKEN_VALUE \t"
+
+ match, _ = get_tokens("#cmakedefine TOKEN_KEY TOKEN_VALUE", magic_string)
+ assert match
+ groups = match.groups()
+ assert groups[1] == magic_string
+ assert groups[3] == "TOKEN_KEY"
+ assert groups[5] == "TOKEN_VALUE"
+
+ match, _ = get_tokens("#cmakedefine TOKEN_KEY @TOKEN_VALUE@", magic_string)
+ assert match
+ groups = match.groups()
+ assert len(groups) == 6
+ assert groups[1] == magic_string
+ assert groups[3] == "TOKEN_KEY"
+ assert groups[5] == "@TOKEN_VALUE@"
+
+ match, _ = get_tokens("#cmakedefine TOKEN_KEY ${TOKEN_VALUE}", magic_string)
+ assert match
+ groups = match.groups()
+ assert len(groups) == 6
+ assert groups[1] == magic_string
+ assert groups[3] == "TOKEN_KEY"
+ assert groups[5] == "${TOKEN_VALUE}"
+
+ match, _ = get_tokens("#cmakedefine TOKEN_KEY", magic_string)
+ assert match
+ groups = match.groups()
+ assert len(groups) == 6
+ assert groups[1] == magic_string
+ assert groups[3] == "TOKEN_KEY"
+ assert groups[5] == ""
+
+ match, _ = get_tokens("#differnt_magic_string TOKEN_KEY", magic_string)
+ assert not match
+
+ match, is_01 = get_tokens(f"#{magic_string}01 TOKEN_KEY", magic_string)
+ assert match
+ assert is_01
+
+ match, _ = get_tokens(f'#cmakedefine FOO "@FOO@"', "cmakedefine")
+ groups = match.groups()
+ assert groups[5] == '"@FOO@"'
+
+
+def test_handle_01():
+ line = "#cmakedefine01 FOO"
+ tokens, _ = get_tokens(line, "cmakedefine")
+ assert handle01(line, tokens, True) == "#define FOO 1"
+ assert handle01(line, tokens, False) == "#define FOO 0"
+
+
+def test_undefine():
+ lines = [
+ "#cmakedefine FOO",
+ "#cmakedefine FOO foo",
+ "#cmakedefine FOO ${FOO}",
+ "#cmakedefine FOO @FOO@",
+ ]
+ for line in lines:
+ tokens, _ = get_tokens(line, "cmakedefine")
+ assert undefine(tokens) == "/* #undef FOO */"
+
+
+def test_replace_value():
+ line = "#cmakedefine FOO whatever@VVV@${FFF}"
+ tokens, _ = get_tokens(line, "cmakedefine")
+ groups = tokens.groups()
+ assert len(groups) == 6
+ assert groups[1] == "cmakedefine"
+ assert groups[3] == "FOO"
+ assert groups[5] == "whatever@VVV@${FFF}"
+ assert replace_value(tokens, "FOO", "VALUE") == "#define FOO VALUE"
+
+
+def test_compute_value():
+ token = "@KEY1@foo${KEY2}@KEY3@@KEY4@"
+ key_value_dict = {
+ "KEY1": "value1",
+ "KEY2": "value2",
+ "KEY3": "value3",
+ "KEY4": "value4",
+ "KEY5": "0",
+ }
+
+ assert compute_value(token, True, key_value_dict) == "value1foo${KEY2}value3value4"
+ assert compute_value(token, False, key_value_dict) == "value1foovalue2value3value4"
+ assert compute_value("@KEY5@", True, key_value_dict) == "0"
+
+
+@given(text())
+def test_substitution_with_given_value(s):
+ kv = {"FOO": s}
+ assert compute_value("@FOO@", True, kv) == s
+ assert compute_value("${FOO}", False, kv) == s
+ assert compute_value("${FOO}", True, kv) == "${FOO}"
diff --git a/tests/test_cases/deps/TARGETS b/tests/test_cases/deps/TARGETS
new file mode 100644
index 0000000..0ac2f3e
--- /dev/null
+++ b/tests/test_cases/deps/TARGETS
@@ -0,0 +1,419 @@
+{ "private":
+ { "type": ["test_rules", "test_case"]
+ , "name": ["deps_private"]
+ , "targets":
+ [ "+foo"
+ , "-main_includes_foo"
+ , "+main_links_foo"
+ , "+main_links_bar_foo"
+ , "+install_bar"
+ ]
+ , "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"
+ , "test -f install_bar/lib/bar/libbar.a"
+ , "test -f install_bar/include/bar/bar.hpp"
+ , "test -f install_bar/lib/foo/libfoo.a"
+ , "! test -f install_bar/include/foo/foo.hpp"
+ ]
+ , "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"
+ , "+install_bar"
+ ]
+ , "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"
+ , "test -f install_bar/lib/bar/libbar.a"
+ , "test -f install_bar/include/bar/bar.hpp"
+ , "test -f install_bar/lib/foo/libfoo.a"
+ , "test -f install_bar/include/foo/foo.hpp"
+ ]
+ , "data": [["TREE", null, "public"]]
+ }
+, "shared":
+ { "type": ["test_rules", "test_case"]
+ , "name": ["deps_shared"]
+ , "targets":
+ [ "+foo"
+ , "+bar"
+ , "+main_uses_foo"
+ , "+test_uses_foo"
+ , "+main_uses_bar"
+ , "+test_uses_bar"
+ , "+test_uses_bar_s"
+ , "+test_uses_baz"
+ , "+test_uses_main"
+ , "+test_diamond"
+ , "+install_foo"
+ , "+install_bar_s"
+ , "+install_baz"
+ , "+install_main"
+ ]
+ , "asserts":
+ [ "test -f foo/libfoo.so.1.2.3"
+ , "test -f foo/foo/foo.hpp"
+ , "test -f bar/libbar.so"
+ , "test -f bar/bar/bar.hpp"
+ , "! test -f bar/foo/foo.hpp"
+ , "test -f install_foo/lib/libfoo.so.1.2.3"
+ , "test -f install_foo/include/foo/foo.hpp"
+ , "grep 'Name: foo' install_foo/lib/pkgconfig/foo.pc"
+ , "grep 'Version: 1.2.3' install_foo/lib/pkgconfig/foo.pc"
+ , "grep -- '-L${libdir}' install_foo/lib/pkgconfig/foo.pc"
+ , "grep -- '-l:libfoo.so.1.2.3' install_foo/lib/pkgconfig/foo.pc"
+ , "test -f install_bar_s/lib/bar/libbar.a"
+ , "test -f install_bar_s/include/bar/bar.hpp"
+ , "test -f install_bar_s/lib/libfoo.so.1.2.3"
+ , "! test -f install_bar_s/include/foo/foo.hpp"
+ , "grep 'Name: bar' install_bar_s/lib/pkgconfig/bar.pc"
+ , "grep '${libdir}/bar/libbar.a' install_bar_s/lib/pkgconfig/bar.pc"
+ , "grep '${libdir}/libfoo.so.1.2.3' install_bar_s/lib/pkgconfig/bar.pc"
+ , "test -f install_baz/lib/libbaz.so"
+ , "test -f install_baz/include/baz/baz.hpp"
+ , "! test -f install_baz/lib/foo/libfoo.a"
+ , "! test -f install_baz/include/foo/foo.hpp"
+ , "./install_main/bin/main_uses_bar | grep main"
+ , "./install_main/bin/main_uses_bar | grep bar"
+ , "./install_main/bin/main_uses_bar | grep foo"
+ , "! test -d install_main/include"
+ ]
+ , "data": [["TREE", null, "shared"]]
+ }
+, "object":
+ { "type": ["test_rules", "test_case"]
+ , "name": ["deps_object"]
+ , "targets": ["+foo", "+bar", "+baz", "+main", "+test_main", "+install_main"]
+ , "asserts":
+ [ "[ -f ./foo/libfoo.so ]"
+ , "[ -f ./bar/bar/bar.o ]"
+ , "[ -f ./baz/libbaz.so ]"
+ , "[ -f ./install_main/lib/libfoo.so ]"
+ , "[ ! -f ./install_main/lib/bar/bar.o ]"
+ , "[ \"$(./install_main/bin/main)\" = 'Hello World and Galaxy' ]"
+ ]
+ , "data": [["TREE", null, "object"]]
+ }
+, "prebuilt_tests":
+ { "type": "tree"
+ , "deps":
+ [ ["test_cases/deps/prebuilt", "foo.hpp"]
+ , ["test_cases/deps/prebuilt", "bar.hpp"]
+ , ["test_cases/deps/prebuilt", "TARGETS"]
+ , ["TREE", null, "shared"]
+ ]
+ }
+, "prebuilt":
+ { "type": ["test_rules", "test_case"]
+ , "name": ["deps_prebuilt"]
+ , "targets":
+ [ "+foo"
+ , "+bar"
+ , "+main_uses_foo"
+ , "+test_uses_foo"
+ , "+main_uses_bar"
+ , "+test_uses_bar"
+ , "+test_uses_bar_s"
+ , "+test_uses_main"
+ , "+install_foo"
+ , "+install_bar"
+ , "+install_main"
+ , "+test_uses_foobar"
+ , "+test_uses_foobar_s"
+ , "+install_foobar"
+ ]
+ , "asserts":
+ [ "test -f foo/libfoo.so.1.2.3"
+ , "test -f foo/foo/foo.hpp"
+ , "test -f bar/libbar.so"
+ , "test -f bar/bar/bar.hpp"
+ , "test -f install_foo/lib/pkgconfig/foo/foo.cflags"
+ , "test -f install_foo/lib/pkgconfig/foo/foo.ldflags"
+ , "test -f install_bar/lib/pkgconfig/foo/foo.cflags"
+ , "test -f install_bar/lib/pkgconfig/foo/foo.ldflags"
+ , "test -f install_bar/lib/pkgconfig/bar/bar.cflags"
+ , "test -f install_bar/lib/pkgconfig/bar/bar.ldflags"
+ , "./install_main/bin/main_uses_bar | grep main"
+ , "./install_main/bin/main_uses_bar | grep bar"
+ , "./install_main/bin/main_uses_bar | grep foo"
+ , "test -f install_foobar/lib/libfoo.so.1.2.3"
+ , "test -f install_foobar/lib/libbar.so"
+ , "test -f install_foobar/lib/pkgconfig/foobar.pc"
+ , "grep 'Cflags:.*@${prefix}/lib/pkgconfig/bar/foobar.cflags' install_foobar/lib/pkgconfig/foobar.pc"
+ , "grep -- '-DUSE_BAR=1 -DUSE_FOO=1' install_foobar/lib/pkgconfig/bar/foobar.cflags"
+ , "grep 'Libs:.*@${prefix}/lib/pkgconfig/bar/foobar.ldflags' install_foobar/lib/pkgconfig/foobar.pc"
+ , "grep -- '-lm -lpthread' install_foobar/lib/pkgconfig/bar/foobar.ldflags"
+ ]
+ , "data": ["prebuilt_tests"]
+ }
+, "cmake":
+ { "type": ["test_rules", "test_case"]
+ , "name": ["deps_cmake"]
+ , "libs": ["googletest", "libz", "libcurl"]
+ , "targets":
+ [ "+gtest"
+ , "+test"
+ , "+gtest_main"
+ , "+testbin"
+ , "+shell_test"
+ , "+install_gtest"
+ , "+install_gtest_main"
+ , "+install_testbin"
+ , "+install_libcurl"
+ ]
+ , "asserts":
+ [ "test -f gtest/libgtest.a"
+ , "test -f gtest/gtest/gtest.h"
+ , "grep 'PASSED.*1 test' test/stdout"
+ , "test -f gtest_main/libgtest.so.1.13.0"
+ , "test -f gtest_main/libgtest_main.so.1.13.0"
+ , "test -f gtest_main/gtest/gtest.h"
+ , "test -f ./testbin/test"
+ , "grep 'PASSED.*1 test' shell_test/stdout"
+ , "test -f install_gtest/lib/libgtest.a"
+ , "test -f install_gtest/include/gtest/gtest.h"
+ , "grep 'Cflags.*lib/pkgconfig/gtest.cflags' install_gtest/lib/pkgconfig/gtest.pc"
+ , "grep 'Libs.*libgtest.a' install_gtest/lib/pkgconfig/gtest.pc"
+ , "grep 'Libs.*lib/pkgconfig/gtest.ldflags' install_gtest/lib/pkgconfig/gtest.pc"
+ , "test -f install_gtest_main/lib/libgtest.so.1.13.0"
+ , "test -f install_gtest_main/lib/libgtest_main.so.1.13.0"
+ , "test -f install_gtest_main/include/gtest/gtest.h"
+ , "grep 'Cflags.*lib/pkgconfig/gtest_main.cflags' install_gtest_main/lib/pkgconfig/gtest_main.pc"
+ , "grep 'Libs.*libgtest.so.1.13.0' install_gtest_main/lib/pkgconfig/gtest_main.pc"
+ , "grep 'Libs.*libgtest_main.so.1.13.0' install_gtest_main/lib/pkgconfig/gtest_main.pc"
+ , "grep 'Libs.*lib/pkgconfig/gtest_main.ldflags' install_gtest_main/lib/pkgconfig/gtest_main.pc"
+ , "./install_testbin/bin/test | grep 'PASSED.*1 test'"
+ , "path=$(ldd install_libcurl/lib/libcurl.so.4.8.0 | awk '/libz/{print $3}') && test -z \"${path##$(pwd)*}\""
+ ]
+ , "data": [["TREE", null, "cmake"]]
+ }
+, "install":
+ { "type": ["test_rules", "test_case"]
+ , "name": ["deps_install"]
+ , "targets":
+ [ "+install_bar_release"
+ , "+install_main_release"
+ , "+install_bar_debug"
+ , "+install_main_debug"
+ , "+install_bar_debug_slim"
+ , "+install_main_debug_slim"
+ ]
+ , "asserts":
+ [ "test -f install_bar_release/lib/bar/libbar.a"
+ , "test -f install_bar_release/lib/foo/libfoo.a"
+ , "test -f install_bar_release/lib/baz/libbaz.a"
+ , "test -f install_bar_release/lib/pkgconfig/bar.pc"
+ , "test -f install_bar_release/include/bar/bar.hpp"
+ , "test -f install_bar_release/include/foo/foo.hpp"
+ , "! test -f install_bar_release/include/baz/baz.hpp"
+ , "! test -d install_bar_release/work"
+ , "test -f install_main_release/bin/main"
+ , "! test -f install_main_release/lib/bar/libbar.a"
+ , "! test -f install_main_release/lib/foo/libfoo.a"
+ , "! test -f install_main_release/lib/baz/libbaz.a"
+ , "! test -d install_main_release/lib/pkgconfig"
+ , "! test -d install_main_release/include"
+ , "! test -d install_main_release/work"
+ , "test -f install_bar_debug/lib/bar/libbar.a"
+ , "test -f install_bar_debug/lib/foo/libfoo.a"
+ , "test -f install_bar_debug/lib/baz/libbaz.a"
+ , "test -f install_bar_debug/lib/pkgconfig/bar.pc"
+ , "test -f install_bar_debug/include/bar/bar.hpp"
+ , "test -f install_bar_debug/include/foo/foo.hpp"
+ , "test -f install_bar_debug/include/baz/baz.hpp"
+ , "test -f install_bar_debug/work/bar/bar.cpp"
+ , "test -f install_bar_debug/work/bar/bar.hpp"
+ , "test -f install_bar_debug/work/foo/foo.cpp"
+ , "test -f install_bar_debug/work/foo/foo.hpp"
+ , "test -f install_bar_debug/work/foo/qux.hpp"
+ , "test -f install_bar_debug/work/baz/baz.cpp"
+ , "test -f install_bar_debug/work/baz/baz.hpp"
+ , "test -f install_main_debug/bin/main"
+ , "test -f install_main_debug/include/bar/bar.hpp"
+ , "test -f install_main_debug/include/foo/foo.hpp"
+ , "test -f install_main_debug/include/baz/baz.hpp"
+ , "test -f install_main_debug/work/bar/bar.cpp"
+ , "test -f install_main_debug/work/bar/bar.hpp"
+ , "test -f install_main_debug/work/foo/foo.cpp"
+ , "test -f install_main_debug/work/foo/foo.hpp"
+ , "test -f install_main_debug/work/foo/qux.hpp"
+ , "test -f install_main_debug/work/baz/baz.cpp"
+ , "test -f install_main_debug/work/baz/baz.hpp"
+ , "test -f install_main_debug/work/main.cpp"
+ , "! test -f install_main_debug/lib/bar/libbar.a"
+ , "! test -f install_main_debug/lib/foo/libfoo.a"
+ , "! test -f install_main_debug/lib/baz/libbaz.a"
+ , "! test -d install_main_debug/lib/pkgconfig"
+ , "test -f install_bar_debug_slim/lib/bar/libbar.a"
+ , "test -f install_bar_debug_slim/lib/foo/libfoo.a"
+ , "test -f install_bar_debug_slim/lib/baz/libbaz.a"
+ , "test -f install_bar_debug_slim/lib/pkgconfig/bar.pc"
+ , "test -f install_bar_debug_slim/include/bar/bar.hpp"
+ , "test -f install_bar_debug_slim/include/foo/foo.hpp"
+ , "! test -f install_bar_debug_slim/include/baz/baz.hpp"
+ , "! test -d install_bar_debug_slim/work"
+ , "test -f install_main_debug_slim/bin/main"
+ , "! test -f install_main_debug_slim/lib/bar/libbar.a"
+ , "! test -f install_main_debug_slim/lib/foo/libfoo.a"
+ , "! test -f install_main_debug_slim/lib/baz/libbaz.a"
+ , "! test -d install_main_debug_slim/lib/pkgconfig"
+ , "! test -d install_main_debug_slim/include"
+ , "! test -d install_main_debug_slim/work"
+ ]
+ , "data": [["TREE", null, "install"]]
+ }
+, "components":
+ { "type": ["test_rules", "test_case"]
+ , "name": ["deps_components"]
+ , "targets":
+ [ "+foo"
+ , "+bar"
+ , "+combined_static_lib"
+ , "+combined_shared_lib"
+ , "+main"
+ , "+main-dynamic"
+ , "+installed_static"
+ , "+installed_shared"
+ ]
+ , "asserts":
+ [ "test -f foo/foo.hpp"
+ , "test -f foo/libfoo.a"
+ , "! test -e foo/libfoo.so"
+ , "test -f bar/bar.hpp"
+ , "test -f bar/libbar.a"
+ , "! test -e bar/libbar.so"
+ , "test -f combined_static_lib/libcombstatic.a"
+ , "test -f combined_static_lib/foo.hpp"
+ , "test -f combined_static_lib/bar.hpp"
+ , "! test -f combined_static_lib/libfoo.a"
+ , "! test -f combined_static_lib/libbar.a"
+ , "! test -f combined_static_lib/foodep.hpp"
+ , "! test -f combined_static_lib/foo.o"
+ , "! test -f combined_static_lib/bar.o"
+ , "test -f combined_shared_lib/libcombshared.so"
+ , "test -f combined_shared_lib/foo.hpp"
+ , "test -f combined_shared_lib/bar.hpp"
+ , "! test -f combined_shared_lib/foodep.hpp"
+ , "! test -f combined_shared_lib/libfoo.a"
+ , "! test -f combined_shared_lib/libbar.a"
+ , "! test -f combined_shared_lib/libfoo.so"
+ , "! test -f combined_shared_lib/libbar.so"
+ , "! test -f combined_shared_lib/foo.o"
+ , "! test -f combined_shared_lib/bar.o"
+ , "./main/main"
+ , "./main/main | grep 'Hello-from-main'"
+ , "./main/main | grep 'bar.3'"
+ , "./main/main | grep 'foo.15'"
+ , "./main-dynamic/bin/main"
+ , "./main-dynamic/bin/main | grep 'Hello-from-main'"
+ , "./main-dynamic/bin/main | grep 'bar.3'"
+ , "./main-dynamic/bin/main | grep 'foo.15'"
+ , "test -f installed_static/include/bar.hpp"
+ , "test -f installed_static/include/foo.hpp"
+ , "test -f installed_static/include/foodep.hpp"
+ , "test -f installed_static/lib/libcombstatic.a"
+ , "test -f installed_static/lib/libfoodep.a"
+ , "! test -f installed_static/lib/libbar.a"
+ , "! test -f installed_static/lib/libfoo.a"
+ , "test -f installed_shared/include/bar.hpp"
+ , "test -f installed_shared/include/foo.hpp"
+ , "test -f installed_shared/include/foodep.hpp"
+ , "test -f installed_shared/lib/libcombshared.so"
+ , "! test -f installed_shared/lib/libfoodep.a"
+ , "! test -f installed_shared/lib/libfoodep.so"
+ , "! test -f installed_shared/lib/libbar.a"
+ , "! test -f installed_shared/lib/libbar.so"
+ , "! test -f installed_shared/lib/libfoo.a"
+ , "! test -f installed_shared/lib/libfoo.so"
+ ]
+ , "data": [["TREE", null, "components"]]
+ }
+, "transitive-components":
+ { "type": ["test_rules", "test_case"]
+ , "name": ["deps_transitive_components"]
+ , "targets":
+ ["+baz", "+bar", "+foo", "+main", "+shared-foo", "+installed-shared-main"]
+ , "asserts":
+ [ "test -f foo/libfoo.a"
+ , "test -f foo/foo.hpp"
+ , "test -f foo/bar.hpp"
+ , "test -f foo/baz.hpp"
+ , "! test -f foo/foodep.hpp"
+ , "! test -f foo/bardep.hpp"
+ , "! test -f foo/bazdep.hpp"
+ , "./main/main"
+ , "./main/main | grep main"
+ , "./main/main | grep foodep"
+ , "./main/main | grep bardep"
+ , "./main/main | grep bazdep"
+ , "test -f shared-foo/libfoo.so"
+ , "test -f shared-foo/foo.hpp"
+ , "test -f shared-foo/bar.hpp"
+ , "test -f shared-foo/baz.hpp"
+ , "! test -f shared-foo/foodep.hpp"
+ , "! test -f shared-foo/bardep.hpp"
+ , "! test -f shared-foo/bazdep.hpp"
+ , "./installed-shared-main/bin/main"
+ , "./installed-shared-main/bin/main | grep main"
+ , "./installed-shared-main/bin/main | grep foodep"
+ , "./installed-shared-main/bin/main | grep bardep"
+ , "./installed-shared-main/bin/main | grep bazdep"
+ ]
+ , "data": [["TREE", null, "transitive-components"]]
+ }
+, "lint":
+ { "type": ["test_rules", "test_case"]
+ , "name": ["lint"]
+ , "targets": ["+test", "+test-shared", "+verifier", "+lint", "+lint-shared"]
+ , "asserts":
+ [ "cat lint/report"
+ , "cat lint-shared/report"
+ , "./verifier/expect lint/out/invocations.json foo.hpp foo.cpp foodep.hpp foodep.cpp bar.hpp bar.cpp bardep.hpp bardep.cpp plain.hpp plain.cpp main.cpp"
+ , "./verifier/expect lint-shared/out/invocations.json foo.hpp foo.cpp foodep.hpp foodep.cpp bar.hpp bar.cpp bardep.hpp bardep.cpp plain.hpp plain.cpp main.cpp"
+ ]
+ , "data": [["TREE", null, "lint"]]
+ }
+, "chain":
+ { "type": ["test_rules", "test_case"]
+ , "name": ["chain"]
+ , "targets": ["+main", "+main-with-deps"]
+ , "asserts":
+ [ "./main-with-deps/bin/main | grep 'foo.*fine'"
+ , "./main-with-deps/bin/main | grep 'qux.*ok'"
+ ]
+ , "data": [["TREE", null, "chain"]]
+ }
+, "ALL":
+ { "type": "install"
+ , "deps":
+ [ "private"
+ , "public"
+ , "shared"
+ , "object"
+ , "prebuilt"
+ , "cmake"
+ , "install"
+ , "components"
+ , "transitive-components"
+ , "lint"
+ , "chain"
+ ]
+ , "tainted": ["test"]
+ }
+}
diff --git a/tests/test_cases/deps/chain/TARGETS b/tests/test_cases/deps/chain/TARGETS
new file mode 100644
index 0000000..974df61
--- /dev/null
+++ b/tests/test_cases/deps/chain/TARGETS
@@ -0,0 +1,38 @@
+{ "foo":
+ { "type": ["@", "rules", "CC", "library"]
+ , "name": ["foo"]
+ , "shared": ["yes"]
+ , "hdrs": ["foo.hpp"]
+ , "srcs": ["foo.cpp"]
+ , "deps": ["bar"]
+ }
+, "bar":
+ { "type": ["@", "rules", "CC", "library"]
+ , "name": ["bar"]
+ , "shared": ["yes"]
+ , "hdrs": ["bar.hpp"]
+ , "srcs": ["bar.cpp"]
+ , "deps": ["baz"]
+ }
+, "baz":
+ { "type": ["@", "rules", "CC", "library"]
+ , "name": ["baz"]
+ , "hdrs": ["baz.hpp"]
+ , "srcs": ["baz.cpp"]
+ , "deps": ["qux"]
+ }
+, "qux":
+ { "type": ["@", "rules", "CC", "library"]
+ , "name": ["qux"]
+ , "hdrs": ["qux.hpp"]
+ , "srcs": ["qux.cpp"]
+ }
+, "main":
+ { "type": ["@", "rules", "CC", "binary"]
+ , "name": ["main"]
+ , "srcs": ["main.cpp"]
+ , "private-deps": ["foo", "qux"]
+ }
+, "main-with-deps":
+ {"type": ["@", "rules", "CC", "install-with-deps"], "targets": ["main"]}
+}
diff --git a/tests/test_cases/deps/chain/bar.cpp b/tests/test_cases/deps/chain/bar.cpp
new file mode 100644
index 0000000..e79bcf1
--- /dev/null
+++ b/tests/test_cases/deps/chain/bar.cpp
@@ -0,0 +1,5 @@
+#include "bar.hpp"
+
+#include "baz.hpp"
+
+void print_bar(std::string s) { print_baz("[bar]: " + s); }
diff --git a/tests/test_cases/deps/chain/bar.hpp b/tests/test_cases/deps/chain/bar.hpp
new file mode 100644
index 0000000..cf3cb8c
--- /dev/null
+++ b/tests/test_cases/deps/chain/bar.hpp
@@ -0,0 +1,8 @@
+#ifndef BAR_HPP
+#define BAR_HPP
+
+#include <string>
+
+void print_bar(std::string);
+
+#endif
diff --git a/tests/test_cases/deps/chain/baz.cpp b/tests/test_cases/deps/chain/baz.cpp
new file mode 100644
index 0000000..b22163b
--- /dev/null
+++ b/tests/test_cases/deps/chain/baz.cpp
@@ -0,0 +1,5 @@
+#include "baz.hpp"
+
+#include "qux.hpp"
+
+void print_baz(std::string s) { print_qux("[baz]: " + s); }
diff --git a/tests/test_cases/deps/chain/baz.hpp b/tests/test_cases/deps/chain/baz.hpp
new file mode 100644
index 0000000..f700c9c
--- /dev/null
+++ b/tests/test_cases/deps/chain/baz.hpp
@@ -0,0 +1,8 @@
+#ifndef BAZ_HPP
+#define BAZ_HPP
+
+#include <string>
+
+void print_baz(std::string);
+
+#endif
diff --git a/tests/test_cases/deps/chain/foo.cpp b/tests/test_cases/deps/chain/foo.cpp
new file mode 100644
index 0000000..a981b95
--- /dev/null
+++ b/tests/test_cases/deps/chain/foo.cpp
@@ -0,0 +1,5 @@
+#include "foo.hpp"
+
+#include "bar.hpp"
+
+void print_foo(std::string s) { print_bar("[foo]: " + s); }
diff --git a/tests/test_cases/deps/chain/foo.hpp b/tests/test_cases/deps/chain/foo.hpp
new file mode 100644
index 0000000..9d66a9d
--- /dev/null
+++ b/tests/test_cases/deps/chain/foo.hpp
@@ -0,0 +1,8 @@
+#ifndef FOO_HPP
+#define FOO_HPP
+
+#include <string>
+
+void print_foo(std::string);
+
+#endif
diff --git a/tests/test_cases/deps/chain/main.cpp b/tests/test_cases/deps/chain/main.cpp
new file mode 100644
index 0000000..db539d4
--- /dev/null
+++ b/tests/test_cases/deps/chain/main.cpp
@@ -0,0 +1,7 @@
+#include "foo.hpp"
+#include "qux.hpp"
+
+int main() {
+ print_foo("Everthing is fine.");
+ print_qux("Everthing is ok.");
+}
diff --git a/tests/test_cases/deps/chain/qux.cpp b/tests/test_cases/deps/chain/qux.cpp
new file mode 100644
index 0000000..e0499cc
--- /dev/null
+++ b/tests/test_cases/deps/chain/qux.cpp
@@ -0,0 +1,5 @@
+#include "qux.hpp"
+
+#include <cstdio>
+
+void print_qux(std::string s) { printf("[qux]: %s\n", s.data()); }
diff --git a/tests/test_cases/deps/chain/qux.hpp b/tests/test_cases/deps/chain/qux.hpp
new file mode 100644
index 0000000..2975cad
--- /dev/null
+++ b/tests/test_cases/deps/chain/qux.hpp
@@ -0,0 +1,8 @@
+#ifndef QUX_HPP
+#define QUX_HPP
+
+#include <string>
+
+void print_qux(std::string);
+
+#endif
diff --git a/tests/test_cases/deps/cmake/TARGETS b/tests/test_cases/deps/cmake/TARGETS
new file mode 100644
index 0000000..2c02f53
--- /dev/null
+++ b/tests/test_cases/deps/cmake/TARGETS
@@ -0,0 +1,76 @@
+{ "gtest":
+ { "type": ["@", "rules", "CC/foreign/cmake", "library"]
+ , "name": ["gtest"]
+ , "version": ["1", "13", "0"]
+ , "project": [["@", "googletest", "", "tree"]]
+ , "out_hdr_dirs": ["gtest"]
+ , "out_libs": ["libgtest.a"]
+ , "pkg-config": ["gtest.pc"]
+ }
+, "testlib":
+ { "type": ["@", "rules", "CC", "library"]
+ , "name": ["testlib"]
+ , "shared": ["yes"]
+ , "srcs": ["main.cpp"]
+ , "private-deps": ["gtest"]
+ }
+, "test":
+ { "type": ["@", "rules", "CC/test", "test"]
+ , "name": ["test"]
+ , "private-deps": ["testlib", "gtest"]
+ }
+, "gtest_main":
+ { "type": ["@", "rules", "CC/foreign/cmake", "library"]
+ , "name": ["gtest_main"]
+ , "version": ["1", "13", "0"]
+ , "project": [["@", "googletest", "", "tree"]]
+ , "defines": ["BUILD_SHARED_LIBS=ON"]
+ , "out_hdr_dirs": ["gtest"]
+ , "out_libs": ["libgtest_main.so.1.13.0", "libgtest.so.1.13.0"]
+ , "pkg-config": ["gtest_main.pc", "gtest.pc"]
+ }
+, "testbin":
+ { "type": ["@", "rules", "CC", "binary"]
+ , "name": ["test"]
+ , "srcs": ["test.cpp"]
+ , "private-deps": ["gtest_main"]
+ }
+, "shell_test":
+ { "type": ["@", "rules", "shell/test", "script"]
+ , "name": ["shell_test"]
+ , "test": ["test.sh"]
+ , "deps": ["testbin"]
+ }
+, "install_gtest":
+ {"type": ["@", "rules", "CC", "install-with-deps"], "targets": ["gtest"]}
+, "install_gtest_main":
+ { "type": ["@", "rules", "CC", "install-with-deps"]
+ , "targets": ["gtest_main"]
+ }
+, "install_testbin":
+ {"type": ["@", "rules", "CC", "install-with-deps"], "targets": ["testbin"]}
+, "libz":
+ { "type": ["@", "rules", "CC/foreign/cmake", "library"]
+ , "name": ["libz"]
+ , "version": ["1", "2", "13"]
+ , "project": [["@", "libz", "", "tree"]]
+ , "defines": ["BUILD_SHARED_LIBS=ON"]
+ , "out_hdrs": ["zconf.h", "zlib.h"]
+ , "out_libs": ["libz.so", "libz.so.1", "libz.so.1.2.13"]
+ , "pc_prefix": ["share/pkgconfig"]
+ , "pkg-config": ["zlib.pc"]
+ }
+, "libcurl":
+ { "type": ["@", "rules", "CC/foreign/cmake", "library"]
+ , "name": ["libcurl"]
+ , "version": ["8", "0", "1"]
+ , "project": [["@", "libcurl", "", "tree"]]
+ , "defines": ["BUILD_SHARED_LIBS=ON", "CURL_ENABLE_SSL=OFF", "USE_ZLIB=ON"]
+ , "out_hdr_dirs": ["curl"]
+ , "out_libs": ["libcurl.so.4.8.0"]
+ , "pkg-config": ["libcurl.pc"]
+ , "deps": ["libz"]
+ }
+, "install_libcurl":
+ {"type": ["@", "rules", "CC", "install-with-deps"], "targets": ["libcurl"]}
+}
diff --git a/tests/test_cases/deps/cmake/main.cpp b/tests/test_cases/deps/cmake/main.cpp
new file mode 100644
index 0000000..575a358
--- /dev/null
+++ b/tests/test_cases/deps/cmake/main.cpp
@@ -0,0 +1,8 @@
+#include <gtest/gtest.h>
+
+TEST(CastTest, float) { EXPECT_EQ(42.0f, float(42)); }
+
+int main(int argc, char **argv) {
+ ::testing::InitGoogleTest(&argc, argv);
+ return RUN_ALL_TESTS();
+}
diff --git a/tests/test_cases/deps/cmake/test.cpp b/tests/test_cases/deps/cmake/test.cpp
new file mode 100644
index 0000000..b1811d8
--- /dev/null
+++ b/tests/test_cases/deps/cmake/test.cpp
@@ -0,0 +1,3 @@
+#include <gtest/gtest.h>
+
+TEST(CastTest, double) { EXPECT_EQ(42.0, double(42)); }
diff --git a/tests/test_cases/deps/cmake/test.sh b/tests/test_cases/deps/cmake/test.sh
new file mode 100644
index 0000000..80b7965
--- /dev/null
+++ b/tests/test_cases/deps/cmake/test.sh
@@ -0,0 +1,5 @@
+#!/bin/sh
+
+set -e
+
+./test | grep PASSED
diff --git a/tests/test_cases/deps/components/TARGETS b/tests/test_cases/deps/components/TARGETS
new file mode 100644
index 0000000..62a599c
--- /dev/null
+++ b/tests/test_cases/deps/components/TARGETS
@@ -0,0 +1,55 @@
+{ "combined_static_lib":
+ { "type": ["@", "rules", "CC", "library"]
+ , "name": ["combstatic"]
+ , "components": ["foo", "bar"]
+ }
+, "combined_shared_lib":
+ { "type": ["@", "rules", "CC", "library"]
+ , "name": ["combshared"]
+ , "shared": [""]
+ , "components": ["foo", "bar"]
+ }
+, "foo":
+ { "type": ["@", "rules", "CC", "library"]
+ , "name": ["foo"]
+ , "hdrs": ["foo.hpp"]
+ , "srcs": ["foo.cpp"]
+ , "deps": ["foodep"]
+ }
+, "foodep":
+ { "type": ["@", "rules", "CC", "library"]
+ , "name": ["foodep"]
+ , "hdrs": ["foodep.hpp"]
+ , "srcs": ["foodep.cpp"]
+ }
+, "bar":
+ { "type": ["@", "rules", "CC", "library"]
+ , "name": ["bar"]
+ , "hdrs": ["bar.hpp"]
+ , "srcs": ["bar.cpp"]
+ }
+, "main":
+ { "type": ["@", "rules", "CC", "binary"]
+ , "name": ["main"]
+ , "srcs": ["main.cpp"]
+ , "private-deps": ["combined_static_lib"]
+ }
+, "main-shared":
+ { "type": ["@", "rules", "CC", "binary"]
+ , "name": ["main"]
+ , "srcs": ["main.cpp"]
+ , "private-deps": ["combined_shared_lib"]
+ }
+, "main-dynamic":
+ { "type": ["@", "rules", "CC", "install-with-deps"]
+ , "targets": ["main-shared"]
+ }
+, "installed_static":
+ { "type": ["@", "rules", "CC", "install-with-deps"]
+ , "targets": ["combined_static_lib"]
+ }
+, "installed_shared":
+ { "type": ["@", "rules", "CC", "install-with-deps"]
+ , "targets": ["combined_shared_lib"]
+ }
+}
diff --git a/tests/test_cases/deps/components/bar.cpp b/tests/test_cases/deps/components/bar.cpp
new file mode 100644
index 0000000..525a3a4
--- /dev/null
+++ b/tests/test_cases/deps/components/bar.cpp
@@ -0,0 +1,9 @@
+#include "bar.hpp"
+
+#include <iostream>
+#include <ostream>
+
+int bar(int x) {
+ std::cout << "bar(" << x << ")" << std::endl;
+ return x * 5;
+}
diff --git a/tests/test_cases/deps/components/bar.hpp b/tests/test_cases/deps/components/bar.hpp
new file mode 100644
index 0000000..63f851d
--- /dev/null
+++ b/tests/test_cases/deps/components/bar.hpp
@@ -0,0 +1,6 @@
+#ifndef BAR_HPP
+#define BAR_HPP
+
+int bar(int);
+
+#endif
diff --git a/tests/test_cases/deps/components/foo.cpp b/tests/test_cases/deps/components/foo.cpp
new file mode 100644
index 0000000..163c6e2
--- /dev/null
+++ b/tests/test_cases/deps/components/foo.cpp
@@ -0,0 +1,10 @@
+#include "foo.hpp"
+
+#include "foodep.hpp"
+#include <iostream>
+#include <ostream>
+
+int foo(int x) {
+ std::cout << "foo(" << x << ")" << std::endl;
+ return foodep(x) + 7;
+}
diff --git a/tests/test_cases/deps/components/foo.hpp b/tests/test_cases/deps/components/foo.hpp
new file mode 100644
index 0000000..45c286c
--- /dev/null
+++ b/tests/test_cases/deps/components/foo.hpp
@@ -0,0 +1,8 @@
+#ifndef FOO_HPP
+#define FOO_HPP
+
+#include "foodep.hpp"
+
+foo_t foo(foo_t);
+
+#endif
diff --git a/tests/test_cases/deps/components/foodep.cpp b/tests/test_cases/deps/components/foodep.cpp
new file mode 100644
index 0000000..e26c335
--- /dev/null
+++ b/tests/test_cases/deps/components/foodep.cpp
@@ -0,0 +1,9 @@
+#include "foodep.hpp"
+
+#include <iostream>
+#include <ostream>
+
+foo_t foodep(foo_t x) {
+ std::cout << "foodep(" << x << ")" << std::endl;
+ return x + 2;
+}
diff --git a/tests/test_cases/deps/components/foodep.hpp b/tests/test_cases/deps/components/foodep.hpp
new file mode 100644
index 0000000..ea16bb0
--- /dev/null
+++ b/tests/test_cases/deps/components/foodep.hpp
@@ -0,0 +1,8 @@
+#ifndef FOODEP_HPP
+#define FOODEP_HPP
+
+typedef int foo_t;
+
+foo_t foodep(foo_t);
+
+#endif
diff --git a/tests/test_cases/deps/components/main.cpp b/tests/test_cases/deps/components/main.cpp
new file mode 100644
index 0000000..b6734a2
--- /dev/null
+++ b/tests/test_cases/deps/components/main.cpp
@@ -0,0 +1,10 @@
+#include "foo.hpp"
+#include "bar.hpp"
+
+#include <iostream>
+#include <ostream>
+
+int main(int argc, char **argv) {
+ std::cout << "Hello-from-main" << std::endl;
+ std::cout << foo(bar(3)) << std::endl;
+}
diff --git a/tests/test_cases/deps/install/TARGETS b/tests/test_cases/deps/install/TARGETS
new file mode 100644
index 0000000..c2cab52
--- /dev/null
+++ b/tests/test_cases/deps/install/TARGETS
@@ -0,0 +1,69 @@
+{ "baz":
+ { "type": ["@", "rules", "CC", "library"]
+ , "name": ["baz"]
+ , "hdrs": ["baz.hpp"]
+ , "srcs": ["baz.cpp"]
+ , "stage": ["baz"]
+ }
+, "foo":
+ { "type": ["@", "rules", "CC", "library"]
+ , "name": ["foo"]
+ , "hdrs": ["foo.hpp"]
+ , "private-hdrs": ["qux.hpp"]
+ , "srcs": ["foo.cpp"]
+ , "private-deps": ["baz"]
+ , "stage": ["foo"]
+ }
+, "bar":
+ { "type": ["@", "rules", "CC", "library"]
+ , "name": ["bar"]
+ , "hdrs": ["bar.hpp"]
+ , "srcs": ["bar.cpp"]
+ , "deps": ["foo"]
+ , "stage": ["bar"]
+ }
+, "main":
+ { "type": ["@", "rules", "CC", "binary"]
+ , "name": ["main"]
+ , "srcs": ["main.cpp"]
+ , "private-deps": ["bar"]
+ }
+, "install_bar_release":
+ {"type": ["@", "rules", "CC", "install-with-deps"], "targets": ["bar"]}
+, "install_main_release":
+ {"type": ["@", "rules", "CC", "install-with-deps"], "targets": ["main"]}
+, "bar debug":
+ { "type": "configure"
+ , "target": "bar"
+ , "config":
+ { "type": "let*"
+ , "bindings": [["DEBUG", true], ["ADD_CXXFLAGS", ["-g"]]]
+ , "body": {"type": "env", "vars": ["DEBUG", "ADD_CXXFLAGS"]}
+ }
+ }
+, "main debug":
+ { "type": "configure"
+ , "target": "main"
+ , "config":
+ { "type": "let*"
+ , "bindings": [["DEBUG", true], ["ADD_CXXFLAGS", ["-g"]]]
+ , "body": {"type": "env", "vars": ["DEBUG", "ADD_CXXFLAGS"]}
+ }
+ }
+, "install_bar_debug":
+ {"type": ["@", "rules", "CC", "install-with-deps"], "targets": ["bar debug"]}
+, "install_main_debug":
+ { "type": ["@", "rules", "CC", "install-with-deps"]
+ , "targets": ["main debug"]
+ }
+, "install_bar_debug_slim":
+ { "type": ["@", "rules", "CC", "install-with-deps"]
+ , "skip-debug-stage": ["yes"]
+ , "targets": ["bar debug"]
+ }
+, "install_main_debug_slim":
+ { "type": ["@", "rules", "CC", "install-with-deps"]
+ , "skip-debug-stage": ["yes"]
+ , "targets": ["main debug"]
+ }
+}
diff --git a/tests/test_cases/deps/install/bar.cpp b/tests/test_cases/deps/install/bar.cpp
new file mode 100644
index 0000000..6cc1260
--- /dev/null
+++ b/tests/test_cases/deps/install/bar.cpp
@@ -0,0 +1,7 @@
+#include "bar.hpp"
+#include <iostream>
+
+int bar(Foo *foo) {
+ std::cout << "bar" << std::endl;
+ return (foo == nullptr) ? -1 : foo->foo();
+}
diff --git a/tests/test_cases/deps/install/bar.hpp b/tests/test_cases/deps/install/bar.hpp
new file mode 100644
index 0000000..ec9c165
--- /dev/null
+++ b/tests/test_cases/deps/install/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/install/baz.cpp b/tests/test_cases/deps/install/baz.cpp
new file mode 100644
index 0000000..c2c26f0
--- /dev/null
+++ b/tests/test_cases/deps/install/baz.cpp
@@ -0,0 +1,7 @@
+#include "baz.hpp"
+#include <iostream>
+
+int baz() {
+ std::cout << baz_str() << std::endl;
+ return 0;
+}
diff --git a/tests/test_cases/deps/install/baz.hpp b/tests/test_cases/deps/install/baz.hpp
new file mode 100644
index 0000000..f0f8377
--- /dev/null
+++ b/tests/test_cases/deps/install/baz.hpp
@@ -0,0 +1,10 @@
+#ifndef BAZ_HPP
+#define BAZ_HPP
+
+int baz();
+
+#include <string>
+
+static inline std::string baz_str() { return "baz"; }
+
+#endif
diff --git a/tests/test_cases/deps/install/foo.cpp b/tests/test_cases/deps/install/foo.cpp
new file mode 100644
index 0000000..4a3b17a
--- /dev/null
+++ b/tests/test_cases/deps/install/foo.cpp
@@ -0,0 +1,10 @@
+#include "foo.hpp"
+#include "baz/baz.hpp"
+#include "qux.hpp"
+#include <iostream>
+
+int Foo::foo() {
+ std::cout << "foo & inline " << baz_str() << std::endl;
+ qux();
+ return baz();
+}
diff --git a/tests/test_cases/deps/install/foo.hpp b/tests/test_cases/deps/install/foo.hpp
new file mode 100644
index 0000000..025f3ef
--- /dev/null
+++ b/tests/test_cases/deps/install/foo.hpp
@@ -0,0 +1,8 @@
+#ifndef FOO_HPP
+#define FOO_HPP
+
+struct Foo {
+ int foo();
+};
+
+#endif
diff --git a/tests/test_cases/deps/install/main.cpp b/tests/test_cases/deps/install/main.cpp
new file mode 100644
index 0000000..2e46d75
--- /dev/null
+++ b/tests/test_cases/deps/install/main.cpp
@@ -0,0 +1,10 @@
+// test binary consuming libraries
+
+#include "bar/bar.hpp"
+#include <iostream>
+
+int main() {
+ std::cout << "main" << std::endl;
+ Foo foo{};
+ return bar(&foo);
+}
diff --git a/tests/test_cases/deps/install/qux.hpp b/tests/test_cases/deps/install/qux.hpp
new file mode 100644
index 0000000..b2f1611
--- /dev/null
+++ b/tests/test_cases/deps/install/qux.hpp
@@ -0,0 +1,8 @@
+#ifndef QUX_HPP
+#define QUX_HPP
+
+#include <iostream>
+
+void qux() { std::cout << "qux" << std::endl; }
+
+#endif \ No newline at end of file
diff --git a/tests/test_cases/deps/lint/TARGETS b/tests/test_cases/deps/lint/TARGETS
new file mode 100644
index 0000000..425815b
--- /dev/null
+++ b/tests/test_cases/deps/lint/TARGETS
@@ -0,0 +1,78 @@
+{ "foo":
+ { "type": ["@", "rules", "CC", "library"]
+ , "name": ["foo"]
+ , "hdrs": ["foo.hpp"]
+ , "srcs": ["foo.cpp"]
+ , "deps": ["foodep"]
+ , "components": ["bar"]
+ }
+, "foodep":
+ { "type": ["@", "rules", "CC", "library"]
+ , "name": ["foodep"]
+ , "hdrs": ["foodep.hpp"]
+ , "srcs": ["foodep.cpp"]
+ }
+, "bar":
+ { "type": ["@", "rules", "CC", "library"]
+ , "name": ["bar"]
+ , "hdrs": ["bar.hpp"]
+ , "srcs": ["bar.cpp"]
+ , "deps": ["bardep"]
+ }
+, "bardep":
+ { "type": ["@", "rules", "CC", "library"]
+ , "name": ["bardep"]
+ , "hdrs": ["bardep.hpp"]
+ , "srcs": ["bardep.cpp"]
+ }
+, "plain":
+ { "type": ["@", "rules", "CC", "library"]
+ , "name": ["plain"]
+ , "arguments_config": ["TEST_SHARED"]
+ , "shared":
+ { "type": "if"
+ , "cond": {"type": "var", "name": "TEST_SHARED"}
+ , "then": ["yes"]
+ }
+ , "hdrs": ["plain.hpp"]
+ , "srcs": ["plain.cpp"]
+ , "deps": ["foo"]
+ }
+, "main":
+ { "type": ["@", "rules", "CC", "binary"]
+ , "name": ["main"]
+ , "srcs": ["main.cpp"]
+ , "private-deps": ["plain"]
+ }
+, "test":
+ { "type": ["@", "rules", "shell/test", "script"]
+ , "name": ["test"]
+ , "test": ["check-main.sh"]
+ , "deps": ["main"]
+ }
+, "suite":
+ { "type": ["@", "rules", "test", "suite"]
+ , "deps": ["test"]
+ , "stage": ["suite"]
+ }
+, "lint":
+ { "type": ["@", "rules", "lint", "targets"]
+ , "targets": ["suite"]
+ , "tainted": ["test"]
+ , "linter": ["pretend_lint.py"]
+ , "summarizer": ["summary.py"]
+ }
+, "lint-shared":
+ { "type": "configure"
+ , "target": "lint"
+ , "tainted": ["lint", "test"]
+ , "config": {"type": "singleton_map", "key": "TEST_SHARED", "value": true}
+ }
+, "test-shared":
+ { "type": "configure"
+ , "target": "test"
+ , "tainted": ["test"]
+ , "config": {"type": "singleton_map", "key": "TEST_SHARED", "value": true}
+ }
+, "verifier": {"type": "install", "files": {"expect": "expect.py"}}
+}
diff --git a/tests/test_cases/deps/lint/bar.cpp b/tests/test_cases/deps/lint/bar.cpp
new file mode 100644
index 0000000..437619c
--- /dev/null
+++ b/tests/test_cases/deps/lint/bar.cpp
@@ -0,0 +1,10 @@
+#include "bar.hpp"
+
+#include "bardep.hpp"
+#include <iostream>
+#include <ostream>
+
+int bar(int x) {
+ std::cout << "bar(" << x << ")" << std::endl;
+ return bardep(x) * 7;
+}
diff --git a/tests/test_cases/deps/lint/bar.hpp b/tests/test_cases/deps/lint/bar.hpp
new file mode 100644
index 0000000..e29bfd7
--- /dev/null
+++ b/tests/test_cases/deps/lint/bar.hpp
@@ -0,0 +1,8 @@
+#ifndef BAR_HPP
+#define BAR_HPP
+
+#include "bardep.hpp"
+
+bar_t bar(bar_t);
+
+#endif
diff --git a/tests/test_cases/deps/lint/bardep.cpp b/tests/test_cases/deps/lint/bardep.cpp
new file mode 100644
index 0000000..33931da
--- /dev/null
+++ b/tests/test_cases/deps/lint/bardep.cpp
@@ -0,0 +1,9 @@
+#include "bardep.hpp"
+
+#include <iostream>
+#include <ostream>
+
+bar_t bardep(bar_t x) {
+ std::cout << "bardep(" << x << ")" << std::endl;
+ return x + 5;
+}
diff --git a/tests/test_cases/deps/lint/bardep.hpp b/tests/test_cases/deps/lint/bardep.hpp
new file mode 100644
index 0000000..ebfaef4
--- /dev/null
+++ b/tests/test_cases/deps/lint/bardep.hpp
@@ -0,0 +1,8 @@
+#ifndef BARDEP_HPP
+#define BARDEP_HPP
+
+typedef int bar_t;
+
+bar_t bardep(bar_t);
+
+#endif
diff --git a/tests/test_cases/deps/lint/check-main.sh b/tests/test_cases/deps/lint/check-main.sh
new file mode 100644
index 0000000..1705968
--- /dev/null
+++ b/tests/test_cases/deps/lint/check-main.sh
@@ -0,0 +1,3 @@
+set -eu
+
+./main | grep result:
diff --git a/tests/test_cases/deps/lint/expect.py b/tests/test_cases/deps/lint/expect.py
new file mode 100755
index 0000000..d26343d
--- /dev/null
+++ b/tests/test_cases/deps/lint/expect.py
@@ -0,0 +1,24 @@
+#!/usr/bin/env python3
+
+import json
+import os
+import sys
+
+with open(sys.argv[1]) as f:
+ invocations = json.load(f)
+
+expected = set(sys.argv[2:])
+found = set()
+
+for name, cmd in invocations.items():
+ print("- %s compiled as %r" % (name, cmd))
+ found.add(os.path.basename(name))
+
+if expected != found:
+ print()
+ print("Found: %r" % (found,))
+ print("missing:%r" % (expected - found,))
+ print("unexpected: %r" % (found - expected,))
+ sys.exit(1)
+else:
+ print("OK")
diff --git a/tests/test_cases/deps/lint/foo.cpp b/tests/test_cases/deps/lint/foo.cpp
new file mode 100644
index 0000000..197e1fb
--- /dev/null
+++ b/tests/test_cases/deps/lint/foo.cpp
@@ -0,0 +1,10 @@
+#include "foo.hpp"
+
+#include "foodep.hpp"
+#include <iostream>
+#include <ostream>
+
+int foo(int x) {
+ std::cout << "foo(" << x << ")" << std::endl;
+ return foodep(x) * 3;
+}
diff --git a/tests/test_cases/deps/lint/foo.hpp b/tests/test_cases/deps/lint/foo.hpp
new file mode 100644
index 0000000..45c286c
--- /dev/null
+++ b/tests/test_cases/deps/lint/foo.hpp
@@ -0,0 +1,8 @@
+#ifndef FOO_HPP
+#define FOO_HPP
+
+#include "foodep.hpp"
+
+foo_t foo(foo_t);
+
+#endif
diff --git a/tests/test_cases/deps/lint/foodep.cpp b/tests/test_cases/deps/lint/foodep.cpp
new file mode 100644
index 0000000..e26c335
--- /dev/null
+++ b/tests/test_cases/deps/lint/foodep.cpp
@@ -0,0 +1,9 @@
+#include "foodep.hpp"
+
+#include <iostream>
+#include <ostream>
+
+foo_t foodep(foo_t x) {
+ std::cout << "foodep(" << x << ")" << std::endl;
+ return x + 2;
+}
diff --git a/tests/test_cases/deps/lint/foodep.hpp b/tests/test_cases/deps/lint/foodep.hpp
new file mode 100644
index 0000000..ea16bb0
--- /dev/null
+++ b/tests/test_cases/deps/lint/foodep.hpp
@@ -0,0 +1,8 @@
+#ifndef FOODEP_HPP
+#define FOODEP_HPP
+
+typedef int foo_t;
+
+foo_t foodep(foo_t);
+
+#endif
diff --git a/tests/test_cases/deps/lint/main.cpp b/tests/test_cases/deps/lint/main.cpp
new file mode 100644
index 0000000..7453eb9
--- /dev/null
+++ b/tests/test_cases/deps/lint/main.cpp
@@ -0,0 +1,9 @@
+#include "plain.hpp"
+
+#include <iostream>
+#include <ostream>
+
+int main(int argc, char **argv) {
+ std::cout << "result: " << foobar(1) << std::endl;
+ return 0;
+}
diff --git a/tests/test_cases/deps/lint/plain.cpp b/tests/test_cases/deps/lint/plain.cpp
new file mode 100644
index 0000000..1db123f
--- /dev/null
+++ b/tests/test_cases/deps/lint/plain.cpp
@@ -0,0 +1,5 @@
+#include "plain.hpp"
+
+bar_t foobar(foo_t x) {
+ return bar(static_cast<bar_t>(foo(x)));
+}
diff --git a/tests/test_cases/deps/lint/plain.hpp b/tests/test_cases/deps/lint/plain.hpp
new file mode 100644
index 0000000..20b84f5
--- /dev/null
+++ b/tests/test_cases/deps/lint/plain.hpp
@@ -0,0 +1,9 @@
+#ifndef PLAIN_HPP
+#define PLAIN_HPP
+
+#include "foo.hpp"
+#include "bar.hpp"
+
+bar_t foobar(foo_t);
+
+#endif
diff --git a/tests/test_cases/deps/lint/pretend_lint.py b/tests/test_cases/deps/lint/pretend_lint.py
new file mode 100755
index 0000000..c84285b
--- /dev/null
+++ b/tests/test_cases/deps/lint/pretend_lint.py
@@ -0,0 +1,15 @@
+#!/usr/bin/env python3
+
+import json
+import os
+import subprocess
+import sys
+
+# log the invocation
+with open(os.path.join(os.environ["OUT"], "invocation.json"), "w") as f:
+ json.dump(sys.argv[1:], f)
+
+# verify the given command succeeds
+result = subprocess.run(sys.argv[2:])
+
+sys.exit(result.returncode)
diff --git a/tests/test_cases/deps/lint/summary.py b/tests/test_cases/deps/lint/summary.py
new file mode 100755
index 0000000..361f681
--- /dev/null
+++ b/tests/test_cases/deps/lint/summary.py
@@ -0,0 +1,27 @@
+#!/usr/bin/env python3
+
+import json
+import os
+import sys
+
+status = 0
+invocations = {}
+
+for lint in sorted(os.listdir()):
+ if os.path.isdir(lint):
+ with open(os.path.join(lint, "result")) as f:
+ result = f.read().strip()
+ if result != "PASS":
+ status = 1
+ with open(os.path.join(lint, "stdout")) as f:
+ print(f.read())
+ with open(os.path.join(lint, "stderr")) as f:
+ print(f.read())
+ with open(os.path.join(lint, "out/invocation.json")) as f:
+ invocation = json.load(f)
+ invocations[invocation[0]] = invocation[1:]
+
+with open(os.path.join(os.environ["OUT"], "invocations.json"), "w") as f:
+ json.dump(invocations, f)
+
+sys.exit(status)
diff --git a/tests/test_cases/deps/object/TARGETS b/tests/test_cases/deps/object/TARGETS
new file mode 100644
index 0000000..412d2ea
--- /dev/null
+++ b/tests/test_cases/deps/object/TARGETS
@@ -0,0 +1,49 @@
+{ "foo":
+ { "type": ["@", "rules", "CC", "library"]
+ , "name": ["foo"]
+ , "shared": ["yes"]
+ , "hdrs": ["foo.hpp"]
+ , "srcs": ["foo.cpp"]
+ , "deps": ["bar"]
+ , "stage": ["foo"]
+ }
+, "bar":
+ { "type": "configure"
+ , "target": "bar (plain)"
+ , "config":
+ {"type": "singleton_map", "key": "BUILD_OBJECT_ONLY", "value": "true"}
+ }
+, "bar (plain)":
+ { "type": ["@", "rules", "CC", "library"]
+ , "name": ["bar"]
+ , "hdrs": ["bar.hpp"]
+ , "srcs": ["bar.cpp"]
+ , "stage": ["bar"]
+ }
+, "baz":
+ { "type": ["@", "rules", "CC", "library"]
+ , "name": ["baz"]
+ , "shared": ["yes"]
+ , "deps": ["bar"]
+ , "stage": ["baz"]
+ }
+, "main":
+ { "type": ["@", "rules", "CC", "binary"]
+ , "name": ["main"]
+ , "srcs": ["main.cpp"]
+ , "private-deps": ["foo"]
+ }
+, "test_main":
+ { "type": ["@", "rules", "shell/test", "script"]
+ , "name": ["test_main"]
+ , "test": ["test_main.sh"]
+ , "deps": ["main"]
+ }
+, "test_main.sh":
+ { "type": "file_gen"
+ , "name": "test.sh"
+ , "data": "set -e\n[ \"$(./main)\" = \"Hello World and Galaxy\" ]"
+ }
+, "install_main":
+ {"type": ["@", "rules", "CC", "install-with-deps"], "targets": ["main"]}
+}
diff --git a/tests/test_cases/deps/object/bar.cpp b/tests/test_cases/deps/object/bar.cpp
new file mode 100644
index 0000000..5d7c616
--- /dev/null
+++ b/tests/test_cases/deps/object/bar.cpp
@@ -0,0 +1,3 @@
+#include <string>
+
+std::string bar() { return "and Galaxy"; }
diff --git a/tests/test_cases/deps/object/bar.hpp b/tests/test_cases/deps/object/bar.hpp
new file mode 100644
index 0000000..9c97d54
--- /dev/null
+++ b/tests/test_cases/deps/object/bar.hpp
@@ -0,0 +1,9 @@
+#ifndef BAR_HPP
+#define BAR_HPP
+
+#include <string>
+
+// use this object lib to extend the public symbols of shared lib 'foo' by 'bar'
+std::string bar();
+
+#endif
diff --git a/tests/test_cases/deps/object/foo.cpp b/tests/test_cases/deps/object/foo.cpp
new file mode 100644
index 0000000..9687a6a
--- /dev/null
+++ b/tests/test_cases/deps/object/foo.cpp
@@ -0,0 +1,3 @@
+#include "foo.hpp"
+
+std::string foo() { return "Hello World"; }
diff --git a/tests/test_cases/deps/object/foo.hpp b/tests/test_cases/deps/object/foo.hpp
new file mode 100644
index 0000000..d880c27
--- /dev/null
+++ b/tests/test_cases/deps/object/foo.hpp
@@ -0,0 +1,8 @@
+#ifndef FOO_HPP
+#define FOO_HPP
+
+#include <string>
+
+std::string foo();
+
+#endif
diff --git a/tests/test_cases/deps/object/main.cpp b/tests/test_cases/deps/object/main.cpp
new file mode 100644
index 0000000..cfc2a4f
--- /dev/null
+++ b/tests/test_cases/deps/object/main.cpp
@@ -0,0 +1,8 @@
+#include "bar/bar.hpp"
+#include "foo/foo.hpp"
+#include <iostream>
+
+int main(int argc, char const *argv[]) {
+ std::cout << foo() << " " << bar() << std::endl;
+ return 0;
+}
diff --git a/tests/test_cases/deps/prebuilt/TARGETS b/tests/test_cases/deps/prebuilt/TARGETS
new file mode 100644
index 0000000..c923995
--- /dev/null
+++ b/tests/test_cases/deps/prebuilt/TARGETS
@@ -0,0 +1,158 @@
+{ "foo.pc":
+ { "type": "file_gen"
+ , "name": "foo.pc"
+ , "data":
+ { "type": "join"
+ , "separator": "\n"
+ , "$1":
+ [ "Name: foo"
+ , "Version: 1.2.3"
+ , "Description: test prebuilt foo"
+ , "URL: unknown"
+ , "Cflags: -I/usr/include -DUSE_FOO=1"
+ , "Libs: -L/usr/lib -lfoo -lpthread"
+ ]
+ }
+ }
+, "foo":
+ { "type": ["@", "rules", "CC/prebuilt", "library"]
+ , "name": ["foo"]
+ , "version": ["1", "2", "3"]
+ , "hdrs": ["foo.hpp"]
+ , "lib": [["shared", "foo"]]
+ , "pkg-config": ["foo.pc"]
+ , "stage": ["foo"]
+ }
+, "foo_s":
+ { "type": ["@", "rules", "CC/prebuilt", "library"]
+ , "name": ["foo"]
+ , "version": ["1", "2", "3"]
+ , "hdrs": ["foo.hpp"]
+ , "lib": [["shared", "foo_s"]]
+ , "pkg-config": ["foo.pc"]
+ , "stage": ["foo"]
+ }
+, "bar.pc":
+ { "type": "file_gen"
+ , "name": "bar.pc"
+ , "data":
+ { "type": "join"
+ , "separator": "\n"
+ , "$1":
+ [ "Name: bar"
+ , "Version: unknown"
+ , "Description: test prebuilt bar"
+ , "URL: unknown"
+ , "Cflags: -I/usr/include -DUSE_BAR=1"
+ , "Libs: -L/usr/lib -lbar -lpthread -lm"
+ ]
+ }
+ }
+, "bar":
+ { "type": ["@", "rules", "CC/prebuilt", "library"]
+ , "name": ["bar"]
+ , "hdrs": ["bar.hpp"]
+ , "lib": [["shared", "bar"]]
+ , "pkg-config": ["bar.pc"]
+ , "stage": ["bar"]
+ , "deps": ["foo"]
+ }
+, "bar_s":
+ { "type": ["@", "rules", "CC/prebuilt", "library"]
+ , "name": ["bar"]
+ , "hdrs": ["bar.hpp"]
+ , "lib": [["shared", "bar_s"]]
+ , "pkg-config": ["bar.pc"]
+ , "stage": ["bar"]
+ , "deps": ["foo"]
+ }
+, "main_uses_foo":
+ { "type": ["@", "rules", "CC", "binary"]
+ , "name": ["main_uses_foo"]
+ , "srcs": [["shared", "main_uses_foo.cpp"]]
+ , "private-deps": ["foo"]
+ }
+, "test_uses_foo":
+ { "type": ["@", "rules", "CC/test", "test"]
+ , "name": ["test_uses_foo"]
+ , "srcs": [["shared", "test_uses_foo.cpp"]]
+ , "private-deps": ["foo"]
+ }
+, "main_uses_bar":
+ { "type": ["@", "rules", "CC", "binary"]
+ , "name": ["main_uses_bar"]
+ , "srcs": [["shared", "main_uses_bar.cpp"]]
+ , "private-deps": ["bar"]
+ }
+, "test_uses_bar":
+ { "type": ["@", "rules", "CC/test", "test"]
+ , "name": ["test_uses_bar"]
+ , "srcs": [["shared", "test_uses_bar.cpp"]]
+ , "private-deps": ["bar"]
+ }
+, "test_uses_bar_s":
+ { "type": ["@", "rules", "CC/test", "test"]
+ , "name": ["test_uses_bar"]
+ , "srcs": [["shared", "test_uses_bar.cpp"]]
+ , "private-deps": ["bar_s"]
+ }
+, "test_uses_main":
+ { "type": ["@", "rules", "shell/test", "script"]
+ , "name": ["test_uses_main"]
+ , "test": [["shared", "test_uses_main.sh"]]
+ , "deps": ["main_uses_bar"]
+ }
+, "install_foo":
+ {"type": ["@", "rules", "CC", "install-with-deps"], "targets": ["foo"]}
+, "install_bar":
+ {"type": ["@", "rules", "CC", "install-with-deps"], "targets": ["bar"]}
+, "install_main":
+ { "type": ["@", "rules", "CC", "install-with-deps"]
+ , "targets": ["main_uses_bar"]
+ }
+, "foobar.pc":
+ { "type": "file_gen"
+ , "name": "foobar.pc"
+ , "data":
+ { "type": "join"
+ , "separator": "\n"
+ , "$1":
+ [ "Name: foobar"
+ , "Version: unknown"
+ , "Description: test compound prebuilt foobar"
+ , "URL: unknown"
+ , "Requires: bar, foo >= 1.2.3"
+ ]
+ }
+ }
+, "foobar":
+ { "type": ["@", "rules", "CC/prebuilt", "library"]
+ , "name": ["foobar"]
+ , "hdrs": ["bar.hpp"]
+ , "lib": [["shared", "bar"], ["shared", "foo"]]
+ , "pkg-config": ["foobar.pc", "foo.pc", "bar.pc"]
+ , "stage": ["bar"]
+ }
+, "foobar_s":
+ { "type": ["@", "rules", "CC/prebuilt", "library"]
+ , "name": ["foobar"]
+ , "hdrs": ["bar.hpp"]
+ , "lib": [["shared", "bar_s"], ["shared", "foo_s"]]
+ , "pkg-config": ["foobar.pc", "foo.pc", "bar.pc"]
+ , "stage": ["bar"]
+ }
+, "test_uses_foobar":
+ { "type": ["@", "rules", "CC/test", "test"]
+ , "name": ["test_uses_bar"]
+ , "srcs": [["shared", "test_uses_bar.cpp"]]
+ , "private-deps": ["foobar"]
+ }
+, "test_uses_foobar_s":
+ { "type": ["@", "rules", "CC/test", "test"]
+ , "name": ["test_uses_bar"]
+ , "srcs": [["shared", "test_uses_bar.cpp"]]
+ , "private-deps": ["foobar_s"]
+ }
+, "install_foobar":
+ {"type": ["@", "rules", "CC", "install-with-deps"], "targets": ["foobar"]}
+}
diff --git a/tests/test_cases/deps/prebuilt/bar.hpp b/tests/test_cases/deps/prebuilt/bar.hpp
new file mode 100644
index 0000000..243dba6
--- /dev/null
+++ b/tests/test_cases/deps/prebuilt/bar.hpp
@@ -0,0 +1,8 @@
+#ifndef BAR_HPP
+#define BAR_HPP
+
+#ifdef USE_BAR
+int bar();
+#endif
+
+#endif
diff --git a/tests/test_cases/deps/prebuilt/foo.hpp b/tests/test_cases/deps/prebuilt/foo.hpp
new file mode 100644
index 0000000..4f040d4
--- /dev/null
+++ b/tests/test_cases/deps/prebuilt/foo.hpp
@@ -0,0 +1,8 @@
+#ifndef FOO_HPP
+#define FOO_HPP
+
+#ifdef USE_FOO
+int foo();
+#endif
+
+#endif
diff --git a/tests/test_cases/deps/private/TARGETS b/tests/test_cases/deps/private/TARGETS
new file mode 100644
index 0000000..dd72948
--- /dev/null
+++ b/tests/test_cases/deps/private/TARGETS
@@ -0,0 +1,36 @@
+{ "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"]
+ }
+, "install_bar":
+ {"type": ["@", "rules", "CC", "install-with-deps"], "targets": ["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..bc1807a
--- /dev/null
+++ b/tests/test_cases/deps/public/TARGETS
@@ -0,0 +1,36 @@
+{ "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"]
+ }
+, "install_bar":
+ {"type": ["@", "rules", "CC", "install-with-deps"], "targets": ["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_cases/deps/shared/TARGETS b/tests/test_cases/deps/shared/TARGETS
new file mode 100644
index 0000000..9db7fb8
--- /dev/null
+++ b/tests/test_cases/deps/shared/TARGETS
@@ -0,0 +1,104 @@
+{ "foo":
+ { "type": ["@", "rules", "CC", "library"]
+ , "shared": ["yes"]
+ , "name": ["foo"]
+ , "soversion": ["1", "2", "3"]
+ , "hdrs": ["foo.hpp"]
+ , "srcs": ["foo.cpp"]
+ , "stage": ["foo"]
+ }
+, "foo_s":
+ { "type": ["@", "rules", "CC", "library"]
+ , "name": ["foo"]
+ , "hdrs": ["foo.hpp"]
+ , "srcs": ["foo.cpp"]
+ , "stage": ["foo"]
+ }
+, "bar":
+ { "type": ["@", "rules", "CC", "library"]
+ , "shared": ["yes"]
+ , "name": ["bar"]
+ , "hdrs": ["bar.hpp"]
+ , "srcs": ["bar.cpp"]
+ , "stage": ["bar"]
+ , "private-deps": ["foo"]
+ }
+, "bar_s":
+ { "type": ["@", "rules", "CC", "library"]
+ , "name": ["bar"]
+ , "hdrs": ["bar.hpp"]
+ , "srcs": ["bar.cpp"]
+ , "stage": ["bar"]
+ , "private-deps": ["foo"]
+ }
+, "baz":
+ { "type": ["@", "rules", "CC", "library"]
+ , "name": ["baz"]
+ , "shared": ["yes"]
+ , "hdrs": ["baz.hpp"]
+ , "srcs": ["baz.cpp"]
+ , "stage": ["baz"]
+ , "private-deps": ["foo_s"]
+ }
+, "main_uses_foo":
+ { "type": ["@", "rules", "CC", "binary"]
+ , "name": ["main_uses_foo"]
+ , "srcs": ["main_uses_foo.cpp"]
+ , "private-deps": ["foo"]
+ }
+, "test_uses_foo":
+ { "type": ["@", "rules", "CC/test", "test"]
+ , "name": ["test_uses_foo"]
+ , "srcs": ["test_uses_foo.cpp"]
+ , "private-deps": ["foo"]
+ }
+, "main_uses_bar":
+ { "type": ["@", "rules", "CC", "binary"]
+ , "name": ["main_uses_bar"]
+ , "srcs": ["main_uses_bar.cpp"]
+ , "private-deps": ["bar"]
+ }
+, "test_uses_bar":
+ { "type": ["@", "rules", "CC/test", "test"]
+ , "name": ["test_uses_bar"]
+ , "srcs": ["test_uses_bar.cpp"]
+ , "private-deps": ["bar"]
+ }
+, "test_uses_bar_s":
+ { "type": ["@", "rules", "CC/test", "test"]
+ , "name": ["test_uses_bar_s"]
+ , "srcs": ["test_uses_bar.cpp"]
+ , "private-deps": ["bar_s"]
+ }
+, "test_uses_baz":
+ { "type": ["@", "rules", "CC/test", "test"]
+ , "name": ["test_uses_baz"]
+ , "srcs": ["test_uses_baz.cpp"]
+ , "private-deps": ["baz"]
+ }
+, "test_uses_main":
+ { "type": ["@", "rules", "shell/test", "script"]
+ , "name": ["test_uses_main"]
+ , "test": ["test_uses_main.sh"]
+ , "deps": ["main_uses_bar"]
+ }
+, "test_diamond":
+ { "type": ["@", "rules", "CC/test", "test"]
+ , "name": ["test_diamond"]
+ , "srcs": ["test_diamond.cpp"]
+ , "private-deps": ["foo_s", "baz"]
+ }
+, "install_foo":
+ { "type": ["@", "rules", "CC", "install-with-deps"]
+ , "targets": ["foo"]
+ , "flat-libs": ["yes"]
+ }
+, "install_bar_s":
+ {"type": ["@", "rules", "CC", "install-with-deps"], "targets": ["bar_s"]}
+, "install_baz":
+ {"type": ["@", "rules", "CC", "install-with-deps"], "targets": ["baz"]}
+, "install_main":
+ { "type": ["@", "rules", "CC", "install-with-deps"]
+ , "targets": ["main_uses_bar"]
+ }
+}
diff --git a/tests/test_cases/deps/shared/bar.cpp b/tests/test_cases/deps/shared/bar.cpp
new file mode 100644
index 0000000..dfce0e8
--- /dev/null
+++ b/tests/test_cases/deps/shared/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/shared/bar.hpp b/tests/test_cases/deps/shared/bar.hpp
new file mode 100644
index 0000000..94d4f16
--- /dev/null
+++ b/tests/test_cases/deps/shared/bar.hpp
@@ -0,0 +1,6 @@
+#ifndef BAR_HPP
+#define BAR_HPP
+
+int bar();
+
+#endif
diff --git a/tests/test_cases/deps/shared/baz.cpp b/tests/test_cases/deps/shared/baz.cpp
new file mode 100644
index 0000000..fae4845
--- /dev/null
+++ b/tests/test_cases/deps/shared/baz.cpp
@@ -0,0 +1,8 @@
+#include "foo/foo.hpp"
+#include "baz.hpp"
+#include <iostream>
+
+int baz() {
+ std::cout << "baz\n";
+ return foo();
+}
diff --git a/tests/test_cases/deps/shared/baz.hpp b/tests/test_cases/deps/shared/baz.hpp
new file mode 100644
index 0000000..643cf65
--- /dev/null
+++ b/tests/test_cases/deps/shared/baz.hpp
@@ -0,0 +1,6 @@
+#ifndef BAZ_HPP
+#define BAZ_HPP
+
+int baz();
+
+#endif
diff --git a/tests/test_cases/deps/shared/foo.cpp b/tests/test_cases/deps/shared/foo.cpp
new file mode 100644
index 0000000..f985022
--- /dev/null
+++ b/tests/test_cases/deps/shared/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/shared/foo.hpp b/tests/test_cases/deps/shared/foo.hpp
new file mode 100644
index 0000000..1a28686
--- /dev/null
+++ b/tests/test_cases/deps/shared/foo.hpp
@@ -0,0 +1,6 @@
+#ifndef FOO_HPP
+#define FOO_HPP
+
+int foo();
+
+#endif
diff --git a/tests/test_cases/deps/shared/main_uses_bar.cpp b/tests/test_cases/deps/shared/main_uses_bar.cpp
new file mode 100644
index 0000000..9cfc855
--- /dev/null
+++ b/tests/test_cases/deps/shared/main_uses_bar.cpp
@@ -0,0 +1,8 @@
+#include "bar/bar.hpp"
+#include <iostream>
+
+int main() {
+ std::cout << "main\n";
+ bar();
+ return 0;
+}
diff --git a/tests/test_cases/deps/shared/main_uses_foo.cpp b/tests/test_cases/deps/shared/main_uses_foo.cpp
new file mode 100644
index 0000000..0fb3bb6
--- /dev/null
+++ b/tests/test_cases/deps/shared/main_uses_foo.cpp
@@ -0,0 +1,8 @@
+#include "foo/foo.hpp"
+#include <iostream>
+
+int main() {
+ std::cout << "main\n";
+ foo();
+ return 0;
+}
diff --git a/tests/test_cases/deps/shared/test_diamond.cpp b/tests/test_cases/deps/shared/test_diamond.cpp
new file mode 100644
index 0000000..9298230
--- /dev/null
+++ b/tests/test_cases/deps/shared/test_diamond.cpp
@@ -0,0 +1,4 @@
+#include "baz/baz.hpp"
+#include "foo/foo.hpp"
+
+int main() { return foo() + baz(); }
diff --git a/tests/test_cases/deps/shared/test_uses_bar.cpp b/tests/test_cases/deps/shared/test_uses_bar.cpp
new file mode 100644
index 0000000..c2076ec
--- /dev/null
+++ b/tests/test_cases/deps/shared/test_uses_bar.cpp
@@ -0,0 +1,3 @@
+#include "bar/bar.hpp"
+
+int main() { return bar(); }
diff --git a/tests/test_cases/deps/shared/test_uses_baz.cpp b/tests/test_cases/deps/shared/test_uses_baz.cpp
new file mode 100644
index 0000000..02d67f9
--- /dev/null
+++ b/tests/test_cases/deps/shared/test_uses_baz.cpp
@@ -0,0 +1,3 @@
+#include "baz/baz.hpp"
+
+int main() { return baz(); }
diff --git a/tests/test_cases/deps/shared/test_uses_foo.cpp b/tests/test_cases/deps/shared/test_uses_foo.cpp
new file mode 100644
index 0000000..88feb93
--- /dev/null
+++ b/tests/test_cases/deps/shared/test_uses_foo.cpp
@@ -0,0 +1,3 @@
+#include "foo/foo.hpp"
+
+int main() { return foo(); }
diff --git a/tests/test_cases/deps/shared/test_uses_main.sh b/tests/test_cases/deps/shared/test_uses_main.sh
new file mode 100644
index 0000000..ab8672f
--- /dev/null
+++ b/tests/test_cases/deps/shared/test_uses_main.sh
@@ -0,0 +1,7 @@
+#!/bin/sh
+
+set -e
+
+./main_uses_bar | grep main
+./main_uses_bar | grep bar
+./main_uses_bar | grep foo
diff --git a/tests/test_cases/deps/transitive-components/TARGETS b/tests/test_cases/deps/transitive-components/TARGETS
new file mode 100644
index 0000000..347b94c
--- /dev/null
+++ b/tests/test_cases/deps/transitive-components/TARGETS
@@ -0,0 +1,64 @@
+{ "foo":
+ { "type": ["@", "rules", "CC", "library"]
+ , "name": ["foo"]
+ , "hdrs": ["foo.hpp"]
+ , "srcs": ["foo.cpp"]
+ , "deps": ["foodep"]
+ , "components": ["bar"]
+ }
+, "foodep":
+ { "type": ["@", "rules", "CC", "library"]
+ , "name": ["foodep"]
+ , "hdrs": ["foodep.hpp"]
+ , "srcs": ["foodep.cpp"]
+ }
+, "bar":
+ { "type": ["@", "rules", "CC", "library"]
+ , "name": ["bar"]
+ , "hdrs": ["bar.hpp"]
+ , "srcs": ["bar.cpp"]
+ , "deps": ["bardep"]
+ , "components": ["baz"]
+ }
+, "bardep":
+ { "type": ["@", "rules", "CC", "library"]
+ , "name": ["bardep"]
+ , "hdrs": ["bardep.hpp"]
+ , "srcs": ["bardep.cpp"]
+ }
+, "baz":
+ { "type": ["@", "rules", "CC", "library"]
+ , "name": ["baz"]
+ , "hdrs": ["baz.hpp"]
+ , "srcs": ["baz.cpp"]
+ , "deps": ["bazdep"]
+ }
+, "bazdep":
+ { "type": ["@", "rules", "CC", "library"]
+ , "name": ["bazdep"]
+ , "hdrs": ["bazdep.hpp"]
+ , "srcs": ["bazdep.cpp"]
+ }
+, "main":
+ { "type": ["@", "rules", "CC", "binary"]
+ , "name": ["main"]
+ , "srcs": ["main.cpp"]
+ , "private-deps": ["foo"]
+ }
+, "shared-foo":
+ { "type": ["@", "rules", "CC", "library"]
+ , "name": ["foo"]
+ , "shared": [""]
+ , "components": ["foo"]
+ }
+, "shared-main":
+ { "type": ["@", "rules", "CC", "binary"]
+ , "name": ["main"]
+ , "srcs": ["main.cpp"]
+ , "private-deps": ["shared-foo"]
+ }
+, "installed-shared-main":
+ { "type": ["@", "rules", "CC", "install-with-deps"]
+ , "targets": ["shared-main"]
+ }
+}
diff --git a/tests/test_cases/deps/transitive-components/bar.cpp b/tests/test_cases/deps/transitive-components/bar.cpp
new file mode 100644
index 0000000..437619c
--- /dev/null
+++ b/tests/test_cases/deps/transitive-components/bar.cpp
@@ -0,0 +1,10 @@
+#include "bar.hpp"
+
+#include "bardep.hpp"
+#include <iostream>
+#include <ostream>
+
+int bar(int x) {
+ std::cout << "bar(" << x << ")" << std::endl;
+ return bardep(x) * 7;
+}
diff --git a/tests/test_cases/deps/transitive-components/bar.hpp b/tests/test_cases/deps/transitive-components/bar.hpp
new file mode 100644
index 0000000..e29bfd7
--- /dev/null
+++ b/tests/test_cases/deps/transitive-components/bar.hpp
@@ -0,0 +1,8 @@
+#ifndef BAR_HPP
+#define BAR_HPP
+
+#include "bardep.hpp"
+
+bar_t bar(bar_t);
+
+#endif
diff --git a/tests/test_cases/deps/transitive-components/bardep.cpp b/tests/test_cases/deps/transitive-components/bardep.cpp
new file mode 100644
index 0000000..33931da
--- /dev/null
+++ b/tests/test_cases/deps/transitive-components/bardep.cpp
@@ -0,0 +1,9 @@
+#include "bardep.hpp"
+
+#include <iostream>
+#include <ostream>
+
+bar_t bardep(bar_t x) {
+ std::cout << "bardep(" << x << ")" << std::endl;
+ return x + 5;
+}
diff --git a/tests/test_cases/deps/transitive-components/bardep.hpp b/tests/test_cases/deps/transitive-components/bardep.hpp
new file mode 100644
index 0000000..ebfaef4
--- /dev/null
+++ b/tests/test_cases/deps/transitive-components/bardep.hpp
@@ -0,0 +1,8 @@
+#ifndef BARDEP_HPP
+#define BARDEP_HPP
+
+typedef int bar_t;
+
+bar_t bardep(bar_t);
+
+#endif
diff --git a/tests/test_cases/deps/transitive-components/baz.cpp b/tests/test_cases/deps/transitive-components/baz.cpp
new file mode 100644
index 0000000..0354d55
--- /dev/null
+++ b/tests/test_cases/deps/transitive-components/baz.cpp
@@ -0,0 +1,10 @@
+#include "baz.hpp"
+
+#include "bazdep.hpp"
+#include <iostream>
+#include <ostream>
+
+int baz(int x) {
+ std::cout << "baz(" << x << ")" << std::endl;
+ return bazdep(x) * 13;
+}
diff --git a/tests/test_cases/deps/transitive-components/baz.hpp b/tests/test_cases/deps/transitive-components/baz.hpp
new file mode 100644
index 0000000..926dd6d
--- /dev/null
+++ b/tests/test_cases/deps/transitive-components/baz.hpp
@@ -0,0 +1,8 @@
+#ifndef BAZ_HPP
+#define BAZ_HPP
+
+#include "bazdep.hpp"
+
+baz_t baz(baz_t);
+
+#endif
diff --git a/tests/test_cases/deps/transitive-components/bazdep.cpp b/tests/test_cases/deps/transitive-components/bazdep.cpp
new file mode 100644
index 0000000..c097661
--- /dev/null
+++ b/tests/test_cases/deps/transitive-components/bazdep.cpp
@@ -0,0 +1,9 @@
+#include "bazdep.hpp"
+
+#include <iostream>
+#include <ostream>
+
+baz_t bazdep(baz_t x) {
+ std::cout << "bazdep(" << x << ")" << std::endl;
+ return x + 11;
+}
diff --git a/tests/test_cases/deps/transitive-components/bazdep.hpp b/tests/test_cases/deps/transitive-components/bazdep.hpp
new file mode 100644
index 0000000..d2add97
--- /dev/null
+++ b/tests/test_cases/deps/transitive-components/bazdep.hpp
@@ -0,0 +1,8 @@
+#ifndef BAZDEP_HPP
+#define BAZDEP_HPP
+
+typedef int baz_t;
+
+baz_t bazdep(baz_t);
+
+#endif
diff --git a/tests/test_cases/deps/transitive-components/foo.cpp b/tests/test_cases/deps/transitive-components/foo.cpp
new file mode 100644
index 0000000..197e1fb
--- /dev/null
+++ b/tests/test_cases/deps/transitive-components/foo.cpp
@@ -0,0 +1,10 @@
+#include "foo.hpp"
+
+#include "foodep.hpp"
+#include <iostream>
+#include <ostream>
+
+int foo(int x) {
+ std::cout << "foo(" << x << ")" << std::endl;
+ return foodep(x) * 3;
+}
diff --git a/tests/test_cases/deps/transitive-components/foo.hpp b/tests/test_cases/deps/transitive-components/foo.hpp
new file mode 100644
index 0000000..45c286c
--- /dev/null
+++ b/tests/test_cases/deps/transitive-components/foo.hpp
@@ -0,0 +1,8 @@
+#ifndef FOO_HPP
+#define FOO_HPP
+
+#include "foodep.hpp"
+
+foo_t foo(foo_t);
+
+#endif
diff --git a/tests/test_cases/deps/transitive-components/foodep.cpp b/tests/test_cases/deps/transitive-components/foodep.cpp
new file mode 100644
index 0000000..e26c335
--- /dev/null
+++ b/tests/test_cases/deps/transitive-components/foodep.cpp
@@ -0,0 +1,9 @@
+#include "foodep.hpp"
+
+#include <iostream>
+#include <ostream>
+
+foo_t foodep(foo_t x) {
+ std::cout << "foodep(" << x << ")" << std::endl;
+ return x + 2;
+}
diff --git a/tests/test_cases/deps/transitive-components/foodep.hpp b/tests/test_cases/deps/transitive-components/foodep.hpp
new file mode 100644
index 0000000..ea16bb0
--- /dev/null
+++ b/tests/test_cases/deps/transitive-components/foodep.hpp
@@ -0,0 +1,8 @@
+#ifndef FOODEP_HPP
+#define FOODEP_HPP
+
+typedef int foo_t;
+
+foo_t foodep(foo_t);
+
+#endif
diff --git a/tests/test_cases/deps/transitive-components/main.cpp b/tests/test_cases/deps/transitive-components/main.cpp
new file mode 100644
index 0000000..3e2ae1c
--- /dev/null
+++ b/tests/test_cases/deps/transitive-components/main.cpp
@@ -0,0 +1,11 @@
+#include "foo.hpp"
+#include "bar.hpp"
+#include "baz.hpp"
+
+#include <iostream>
+#include <ostream>
+
+int main(int argc, char **argv) {
+ std::cout << "Hello-from-main" << std::endl;
+ std::cout << foo(bar(baz(13))) << std::endl;
+}
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..5a1baed
--- /dev/null
+++ b/tests/test_rules/README.md
@@ -0,0 +1,28 @@
+# 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 three 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.
+ - `[["@", "test-libs", "", "tree"]]`, which contains a single tree artifact
+ that holds the file trees of external libraries.
+
+## Rule `["test_rules", "test_case"]`
+
+Define a test case for rule tests.
+
+| Field | Description |
+| ----- | ----------- |
+| `"name"` | Name of the test (multiple entries are joined). |
+| `"libs"` | Paths to external libraries' trees provided by the `"test-libs"` repository. From within a test cases, the library's tree can be accessed via `["@", "<libname>", "", "tree"]`. |
+| `"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..3d2aba5
--- /dev/null
+++ b/tests/test_rules/RULES
@@ -0,0 +1,243 @@
+{ "test_case":
+ { "doc":
+ [ "Define a test case for rule tests."
+ , "The config variables \"ENV\", \"ARCH\", \"HOST_ARCH\", \"TARGET_ARCH\","
+ , "\"BUILD_ARCH\", \"OS\" are collected in a file named \"conf_vars.json\"."
+ , "The test runner will pass that file as argument to \"-c\" to the \"just\""
+ , "binary, which is internally called."
+ ]
+ , "string_fields": ["name", "libs", "targets", "asserts"]
+ , "target_fields": ["data"]
+ , "field_doc":
+ { "name": ["Name of the test (multiple entries are joined)."]
+ , "libs":
+ [ "Paths to external libraries' trees provided by the \"test-libs\""
+ , "repository. From within a test cases, the library's tree can be"
+ , "accessed via [\"@\", \"<libpath>\", \"\", \"tree\"]."
+ ]
+ , "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"]
+ , "config_vars":
+ [ "ENV"
+ , "ARCH"
+ , "HOST_ARCH"
+ , "TARGET_ARCH"
+ , "BUILD_ARCH"
+ , "OS"
+ , "TOOLCHAIN_CONFIG"
+ ]
+ , "implicit":
+ { "runner": ["test_runner.py"]
+ , "rules": [["@", "test-rules", "", "tree"]]
+ , "just": [["@", "test-just", "", ""]]
+ , "libs_tree": [["@", "test-libs", "", "tree"]]
+ }
+ , "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"}]
+ , [ "imports"
+ , { "type": "singleton_map"
+ , "key": "imports/TARGETS"
+ , "value":
+ { "type": "BLOB"
+ , "data": "{\"tree\":{\"type\":\"install\",\"dirs\":[[[\"TREE\",null,\".\"],\".\"]]}}"
+ }
+ }
+ ]
+ , ["fieldname", "libs_tree"]
+ , ["location", "libs"]
+ , ["libs", {"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"}]
+ , [ "work_bindings"
+ , { "type": "map_union"
+ , "$1":
+ { "type": "++"
+ , "$1":
+ [ [{"type": "singleton_map", "key": "rules", "value": "rules"}]
+ , { "type": "foreach"
+ , "range": {"type": "FIELD", "name": "libs"}
+ , "var": "libpath"
+ , "body":
+ { "type": "singleton_map"
+ , "key": {"type": "var", "name": "libpath"}
+ , "value": {"type": "var", "name": "libpath"}
+ }
+ }
+ ]
+ }
+ }
+ ]
+ , [ "work_deps"
+ , { "type": "map_union"
+ , "$1":
+ { "type": "++"
+ , "$1":
+ [ [ { "type": "let*"
+ , "bindings":
+ [ ["workspace_root", ["file", "rules"]]
+ , ["rules", {"type": "env", "vars": ["workspace_root"]}]
+ ]
+ , "body": {"type": "env", "vars": ["rules"]}
+ }
+ ]
+ , { "type": "foreach"
+ , "range": {"type": "FIELD", "name": "libs"}
+ , "var": "libpath"
+ , "body":
+ { "type": "let*"
+ , "bindings":
+ [ [ "workspace_root"
+ , [ "file"
+ , { "type": "join"
+ , "$1": ["libs/", {"type": "var", "name": "libpath"}]
+ }
+ ]
+ ]
+ , ["target_root", ["file", "imports"]]
+ ]
+ , "body":
+ { "type": "singleton_map"
+ , "key": {"type": "var", "name": "libpath"}
+ , "value":
+ { "type": "env"
+ , "vars": ["workspace_root", "target_root"]
+ }
+ }
+ }
+ }
+ ]
+ }
+ }
+ ]
+ , [ "repos"
+ , { "type": "singleton_map"
+ , "key": "repos.json"
+ , "value":
+ { "type": "BLOB"
+ , "data":
+ { "type": "json_encode"
+ , "$1":
+ { "type": "let*"
+ , "bindings":
+ [ ["workspace_root", ["file", "work"]]
+ , ["bindings", {"type": "var", "name": "work_bindings"}]
+ , [ "work"
+ , {"type": "env", "vars": ["workspace_root", "bindings"]}
+ ]
+ , [ "repositories"
+ , { "type": "map_union"
+ , "$1":
+ [ {"type": "env", "vars": ["work"]}
+ , {"type": "var", "name": "work_deps"}
+ ]
+ }
+ ]
+ , ["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"]}
+ }
+ }
+ }
+ ]
+ , [ "conf_vars"
+ , { "type": "singleton_map"
+ , "key": "conf_vars.json"
+ , "value":
+ { "type": "BLOB"
+ , "data":
+ { "type": "json_encode"
+ , "$1":
+ { "type": "env"
+ , "vars":
+ [ "ENV"
+ , "ARCH"
+ , "HOST_ARCH"
+ , "TARGET_ARCH"
+ , "BUILD_ARCH"
+ , "OS"
+ , "TOOLCHAIN_CONFIG"
+ ]
+ }
+ }
+ }
+ }
+ ]
+ , [ "results"
+ , { "type": "ACTION"
+ , "inputs":
+ { "type": "map_union"
+ , "$1":
+ [ {"type": "var", "name": "runner"}
+ , {"type": "var", "name": "rules"}
+ , {"type": "var", "name": "just"}
+ , {"type": "var", "name": "imports"}
+ , {"type": "var", "name": "libs"}
+ , {"type": "var", "name": "repos"}
+ , {"type": "var", "name": "work"}
+ , {"type": "var", "name": "config"}
+ , {"type": "var", "name": "conf_vars"}
+ ]
+ }
+ , "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..5c0d129
--- /dev/null
+++ b/tests/test_rules/test_runner.py
@@ -0,0 +1,90 @@
+#!/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: float = time.time()
+time_stop: float = 0
+result: str = "UNKNOWN"
+stderr: str = ""
+stdout: str = ""
+
+
+def dump_results() -> None:
+ 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]),
+ "-c", "conf_vars.json", 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)