diff options
author | Alberto Sartori <alberto.sartori@huawei.com> | 2023-03-15 14:51:13 +0100 |
---|---|---|
committer | Alberto Sartori <alberto.sartori@huawei.com> | 2023-03-15 14:51:13 +0100 |
commit | c3b895900468f34d6808c56b736ddd88933b81ec (patch) | |
tree | d6762dbd3c9326b7d89c1ebcaa5943d5efb101fc | |
parent | dc432708c61d347f78d61bde7721e00a23617f02 (diff) | |
parent | 2e53ff4d37dbda318f9c33e631bbd9a1d27c9b8e (diff) | |
download | rules-cc-c3b895900468f34d6808c56b736ddd88933b81ec.tar.gz |
Merge commit '2e53ff4d37dbda318f9c33e631bbd9a1d27c9b8e' into HEAD
-rw-r--r-- | rules/CC/auto/RULES | 146 | ||||
-rwxr-xr-x | rules/CC/auto/config_runner.py | 76 | ||||
-rw-r--r-- | rules/CC/proto/RULES | 1 |
3 files changed, 223 insertions, 0 deletions
diff --git a/rules/CC/auto/RULES b/rules/CC/auto/RULES index 4f38504..7d27ff6 100644 --- a/rules/CC/auto/RULES +++ b/rules/CC/auto/RULES @@ -952,4 +952,150 @@ } } } +, "config_file": + { "doc": + [ "Generate a C/C++ config header from a given template" + , "" + , "Generate a C/C++ configuration header using defines specified via the" + , "target configuration. In the usual case, a target using this rule is" + , "configured by depending on it from a target that uses the built-in" + , "\"configure\" rule." + ] + , "field_doc": + { "output": + [ "Name of the header file to generate (incl. file name ext). Components are joined with /." + ] + , "input": ["The input configuration file, used as template."] + , "magic_string": + [ "The magic string (e.g., \"cmakedefine\") which identifies in which line" + , "we have to \"#define\" or \"#undef\" variables according to what is" + , "defined in the config field \"defines\"." + ] + , "@only": ["If set, only replace @VAR@ and not ${VAR}"] + } + , "config_doc": + { "defines": + [ "Set a define to a specific value unless its value is \"null\". Must" + , "contain a list of pairs. The first element of each pair is the define" + , "name and the second argument is the value to set. Strings must be" + , "properly escaped. Defines generated from this field are added last," + , "so that they can refer to defines from other \"defines*\" values." + ] + } + , "string_fields": ["magic_string", "@only", "output"] + , "target_fields": ["input"] + , "config_vars": ["defines"] + , "imports": + { "first": "first_list_entry" + , "last": "last_list_entry" + , "stage_singleton_field": ["", "stage_singleton_field"] + } + , "implicit": {"runner": ["config_runner.py"]} + , "expression": + { "type": "let*" + , "bindings": + [ [ "runner" + , { "type": "let*" + , "bindings": [["fieldname", "runner"], ["location", "runner"]] + , "body": + {"type": "CALL_EXPRESSION", "name": "stage_singleton_field"} + } + ] + , [ "dict-defines" + , { "type": "map_union" + , "$1": + { "type": "foreach" + , "range": {"type": "var", "name": "defines", "default": []} + , "var": "pair" + , "body": + { "type": "let*" + , "bindings": + [ ["list", {"type": "var", "name": "pair"}] + , ["key", {"type": "CALL_EXPRESSION", "name": "first"}] + , ["val", {"type": "CALL_EXPRESSION", "name": "last"}] + ] + , "body": + { "type": "singleton_map" + , "key": {"type": "var", "name": "key"} + , "value": {"type": "var", "name": "val"} + } + } + } + } + ] + , [ "magic_string" + , { "type": "assert_non_empty" + , "msg": "A non-empty string has to be provided for magic_string" + , "$1": + {"type": "join", "$1": {"type": "FIELD", "name": "magic_string"}} + } + ] + , [ "@only" + , { "type": "if" + , "cond": {"type": "FIELD", "name": "@only"} + , "then": "true" + , "else": "false" + } + ] + , [ "param-blob" + , { "type": "singleton_map" + , "key": "param-file" + , "value": + { "type": "BLOB" + , "data": + { "type": "json_encode" + , "$1": {"type": "var", "name": "dict-defines"} + } + } + } + ] + , [ "input-blob" + , { "type": "let*" + , "bindings": [["fieldname", "input"], ["location", "input-file"]] + , "body": + {"type": "CALL_EXPRESSION", "name": "stage_singleton_field"} + } + ] + , [ "outfile" + , { "type": "ACTION" + , "inputs": + { "type": "map_union" + , "$1": + [ {"type": "var", "name": "param-blob"} + , {"type": "var", "name": "input-blob"} + , {"type": "var", "name": "runner"} + ] + } + , "cmd": + [ "./runner" + , "input-file" + , "param-file" + , {"type": "var", "name": "magic_string"} + , {"type": "var", "name": "@only"} + ] + , "outs": ["out"] + } + ] + , [ "outfile" + , { "type": "singleton_map" + , "key": + { "type": "join" + , "separator": "/" + , "$1": {"type": "FIELD", "name": "output"} + } + , "value": + { "type": "lookup" + , "key": "out" + , "map": {"type": "var", "name": "outfile"} + } + } + ] + ] + , "body": + { "type": "RESULT" + , "artifacts": {"type": "var", "name": "outfile"} + , "runfiles": {"type": "var", "name": "outfile"} + } + } + } } diff --git a/rules/CC/auto/config_runner.py b/rules/CC/auto/config_runner.py new file mode 100755 index 0000000..f938c27 --- /dev/null +++ b/rules/CC/auto/config_runner.py @@ -0,0 +1,76 @@ +#!/usr/bin/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 re +from sys import argv +from re import sub, match + +input_file = argv[1] +param_file = argv[2] +magic_string = argv[3] +at_only = argv[4] == "true" + + +with open(param_file) as f: + param = json.loads(f.read()) + +# In many cases, CMake simply defines some variables (without any associated +# value). We handle this situation by assigning to the boolean True the empty +# string. Note that no False value should be found, because the right way to set +# a variable to False in the TARGETS file is to *do not mention* that variable +# at all. +for k, v in param.items(): + if isinstance(v, bool): + param[k] = "" + +with open(input_file) as i: + with open("out", "w") as o: + for line in i.readlines(): + if x := re.search( + r"#(.*)(" + magic_string + r" )([ \t]*)([a-zA-Z0-9_]+)", line + ): + # get the VAR + key = x.groups()[-1] + if key in param: + line = sub( + f"{x.group()[1:]}", + f"{x.groups()[0]}define {x.groups()[2]}{key} {param[key]}", + line, + ) + else: + line = f"/* #undef {x.groups()[-1]} */\n" + if x := re.search( + r"#(.*)(" + magic_string + "01 )([ \t]*)([a-zA-Z0-9_]+)", line + ): + # get the VAR + key = x.groups()[-1] + line = sub( + f"{x.group()[1:]}", + f"{x.groups()[0]}define {x.groups()[2]}{key} " + + str(1 if key in param else 0), + line, + ) + if match("#[ \t]*define", line): + if x := re.search(r"@([a-zA-Z0-9-_]+)@", line): + key = x.groups()[0] + line = sub(x.group(), param.get(key, ""), line) + if not at_only: + if x := re.search(r"\${([a-zA-Z0-9-_]+)}", line): + key = x.groups()[0] + line = sub(r"\${" + key + r"}", param.get(key, ""), line) + + print(line, end="", file=o) diff --git a/rules/CC/proto/RULES b/rules/CC/proto/RULES index d1cfc4e..af484b0 100644 --- a/rules/CC/proto/RULES +++ b/rules/CC/proto/RULES @@ -57,6 +57,7 @@ , "defaults": [["./", "..", "defaults"]] , "proto-deps": [ ["@", "grpc", "", "grpc++_codegen_proto"] + , ["@", "grpc", "", "grpc++_codegen_base_src"] , ["@", "protoc", "", "C++ runtime"] ] , "well_known_protos": [["@", "protoc", "", "well_known_protos"]] |