Browse Source

Merge topic 'fileapi-compilerargs'

71a4e34d97 fileAPI: Expose CMAKE_<LANG>_COMPILER_ARG1
1e02926c9a fileAPI: Refactor toolchains schema to prepare for new version
3caa572c05 fileAPI: Output more info for test failures

Acked-by: Kitware Robot <[email protected]>
Merge-request: !11419
Craig Scott 1 day ago
parent
commit
c98bec6ae6

+ 12 - 1
Help/manual/cmake-file-api.7.rst

@@ -2109,12 +2109,13 @@ There is only one ``toolchains`` object major version, version 1.
 
   {
     "kind": "toolchains",
-    "version": { "major": 1, "minor": 0 },
+    "version": { "major": 1, "minor": 1 },
     "toolchains": [
       {
         "language": "C",
         "compiler": {
           "path": "/usr/bin/cc",
+          "commandFragment": "--config x86_64-linux-gnu.cfg",
           "id": "GNU",
           "version": "9.3.0",
           "implicit": {
@@ -2193,6 +2194,16 @@ The members specific to ``toolchains`` objects are:
       :variable:`CMAKE_<LANG>_COMPILER` variable is defined for the current
       language. Its value is a JSON string holding the path to the compiler.
 
+    ``commandFragment``
+      Optional member that is present when the
+      :variable:`CMAKE_<LANG>_COMPILER` variable is a list containing multiple
+      elements or the :envvar:`CC` or similar environment variable contains
+      command line arguments after the compiler executable.
+      Its value is a JSON string holding the second and further elements
+      (mandatory arguments to the compiler) as a command line fragment.
+
+      This field was added in toolchains version 1.1.
+
     ``id``
       Optional member that is present when the
       :variable:`CMAKE_<LANG>_COMPILER_ID` variable is defined for the current

+ 195 - 71
Help/manual/file_api/schema_toolchains.json

@@ -7,13 +7,21 @@
     "version",
     "toolchains"
   ],
-  "properties": {
+  "oneOf": [
+    {
+      "$ref": "#/definitions/toolchainsObjV1_0"
+    },
+    {
+      "$ref": "#/definitions/toolchainsObjV1_1"
+    }
+  ],
+  "definitions": {
     "kind": {
       "type": "string",
       "const": "toolchains",
       "description": "Specifies the object kind"
     },
-    "version": {
+    "versionV1_0": {
       "type": "object",
       "required": [
         "major",
@@ -31,87 +39,203 @@
       },
       "additionalProperties": false
     },
-    "toolchains": {
+    "versionV1_1": {
+      "type": "object",
+      "required": [
+        "major",
+        "minor"
+      ],
+      "properties": {
+        "major": {
+          "type": "integer",
+          "const": 1
+        },
+        "minor": {
+          "type": "integer",
+          "const": 1
+        }
+      },
+      "additionalProperties": false
+    },
+    "language": {
+      "type": "string",
+      "description": "Toolchain language identifier (e.g. C, CXX)"
+    },
+    "sourceFileExtensions": {
       "type": "array",
-      "description": "Array of toolchain configurations per language",
       "items": {
-        "type": "object",
-        "required": [
-          "language",
-          "compiler"
-        ],
-        "properties": {
-          "language": {
-            "type": "string",
-            "description": "Toolchain language identifier (e.g. C, CXX)"
+        "type": "string"
+      },
+      "description": "List of source file extensions (without leading dot) supported by this toolchain"
+    },
+    "compilerPath": {
+      "type": "string",
+      "description": "Path to the compiler executable. This is present when the CMAKE_<LANG>_COMPILER variable is defined."
+    },
+    "compilerCommandFragment": {
+      "type": "string",
+      "description": "Mandatory arguments to the compiler, as a command line fragment. This is present when the CMAKE_<LANG>_COMPILER variable is a list containing multiple elements or the CC or similar environment variable contains command line arguments."
+    },
+    "compilerId": {
+      "type": "string",
+      "description": "Compiler identifier (e.g. GNU, MSVC). This is present when the CMAKE_<LANG>_COMPILER_ID variable is defined."
+    },
+    "compilerVersion": {
+      "type": "string",
+      "description": "Version of the compiler. This is present when the CMAKE_<LANG>_COMPILER_VERSION variable is defined."
+    },
+    "compilerTarget": {
+      "type": "string",
+      "description": "Cross-compiling target of the compiler. This is present when the CMAKE_<LANG>_COMPILER_TARGET variable is defined."
+    },
+    "compilerImplicit": {
+      "type": "object",
+      "properties": {
+        "includeDirectories": {
+          "type": "array",
+          "items": {
+            "type": "string"
+          },
+          "description": "List of implicit include directories for the compiler. This is present when the CMAKE_<LANG>_IMPLICIT_INCLUDE_DIRECTORIES variable is defined."
+        },
+        "linkDirectories": {
+          "type": "array",
+          "items": {
+            "type": "string"
           },
-          "compiler": {
+          "description": "List of implicit link directories for the compiler front end. This is present when the CMAKE_<LANG>_IMPLICIT_LINK_DIRECTORIES variable is defined."
+        },
+        "linkFrameworkDirectories": {
+          "type": "array",
+          "items": {
+            "type": "string"
+          },
+          "description": "List of implicit link framework directories for the compiler front end. This is present when the CMAKE_<LANG>_IMPLICIT_FRAMEWORK_DIRECTORIES variable is defined."
+        },
+        "linkLibraries": {
+          "type": "array",
+          "items": {
+            "type": "string"
+          },
+          "description": "List of implicit link libraries for the compiler front end. This is present when the CMAKE_<LANG>_IMPLICIT_LINK_LIBRARIES variable is defined."
+        }
+      },
+      "additionalProperties": false
+    },
+    "compilerV1_0": {
+      "type": "object",
+      "properties": {
+        "path": {
+          "$ref": "#/definitions/compilerPath"
+        },
+        "id": {
+          "$ref": "#/definitions/compilerId"
+        },
+        "version": {
+          "$ref": "#/definitions/compilerVersion"
+        },
+        "target": {
+          "$ref": "#/definitions/compilerTarget"
+        },
+        "implicit": {
+          "$ref": "#/definitions/compilerImplicit"
+        }
+      },
+      "additionalProperties": false
+    },
+    "compilerV1_1": {
+      "type": "object",
+      "properties": {
+        "path": {
+          "$ref": "#/definitions/compilerPath"
+        },
+        "commandFragment": {
+          "$ref": "#/definitions/compilerCommandFragment"
+        },
+        "id": {
+          "$ref": "#/definitions/compilerId"
+        },
+        "version": {
+          "$ref": "#/definitions/compilerVersion"
+        },
+        "target": {
+          "$ref": "#/definitions/compilerTarget"
+        },
+        "implicit": {
+          "$ref": "#/definitions/compilerImplicit"
+        }
+      },
+      "additionalProperties": false
+    },
+    "toolchainsObjV1_0": {
+      "type": "object",
+      "properties": {
+        "kind": {
+          "$ref": "#/definitions/kind"
+        },
+        "version": {
+          "$ref": "#/definitions/versionV1_0"
+        },
+        "toolchains": {
+          "type": "array",
+          "description": "Array of toolchain configurations per language",
+          "items": {
             "type": "object",
+            "required": [
+              "language",
+              "compiler"
+            ],
             "properties": {
-              "path": {
-                "type": "string",
-                "description": "Path to the compiler executable. This is present when the CMAKE_<LANG>_COMPILER variable is defined."
+              "language": {
+                "$ref": "#/definitions/language"
               },
-              "id": {
-                "type": "string",
-                "description": "Compiler identifier (e.g. GNU, MSVC). This is present when the CMAKE_<LANG>_COMPILER_ID variable is defined."
+              "compiler": {
+                "$ref": "#/definitions/compilerV1_0"
               },
-              "version": {
-                "type": "string",
-                "description": "Version of the compiler. This is present when the CMAKE_<LANG>_COMPILER_VERSION variable is defined."
+              "sourceFileExtensions": {
+                "$ref": "#/definitions/sourceFileExtensions"
+              }
+            },
+            "additionalProperties": false
+          }
+        }
+      },
+      "additionalProperties": false
+    },
+    "toolchainsObjV1_1": {
+      "type": "object",
+      "properties": {
+        "kind": {
+          "$ref": "#/definitions/kind"
+        },
+        "version": {
+          "$ref": "#/definitions/versionV1_1"
+        },
+        "toolchains": {
+          "type": "array",
+          "description": "Array of toolchain configurations per language",
+          "items": {
+            "type": "object",
+            "required": [
+              "language",
+              "compiler"
+            ],
+            "properties": {
+              "language": {
+                "$ref": "#/definitions/language"
               },
-              "target": {
-                "type": "string",
-                "description": "Cross-compiling target of the compiler. This is present when the CMAKE_<LANG>_COMPILER_TARGET variable is defined."
+              "compiler": {
+                "$ref": "#/definitions/compilerV1_1"
               },
-              "implicit": {
-                "type": "object",
-                "properties": {
-                  "includeDirectories": {
-                    "type": "array",
-                    "items": {
-                      "type": "string"
-                    },
-                    "description": "List of implicit include directories for the compiler. This is present when the CMAKE_<LANG>_IMPLICIT_INCLUDE_DIRECTORIES variable is defined."
-                  },
-                  "linkDirectories": {
-                    "type": "array",
-                    "items": {
-                      "type": "string"
-                    },
-                    "description": "List of implicit link directories for the compiler front end. This is present when the CMAKE_<LANG>_IMPLICIT_LINK_DIRECTORIES variable is defined."
-                  },
-                  "linkFrameworkDirectories": {
-                    "type": "array",
-                    "items": {
-                      "type": "string"
-                    },
-                    "description": "List of implicit link framework directories for the compiler front end. This is present when the CMAKE_<LANG>_IMPLICIT_FRAMEWORK_DIRECTORIES variable is defined."
-                  },
-                  "linkLibraries": {
-                    "type": "array",
-                    "items": {
-                      "type": "string"
-                    },
-                    "description": "List of implicit link libraries for the compiler front end. This is present when the CMAKE_<LANG>_IMPLICIT_LINK_LIBRARIES variable is defined."
-                  }
-                },
-                "additionalProperties": false
+              "sourceFileExtensions": {
+                "$ref": "#/definitions/sourceFileExtensions"
               }
             },
             "additionalProperties": false
-          },
-          "sourceFileExtensions": {
-            "type": "array",
-            "items": {
-              "type": "string"
-            },
-            "description": "List of source file extensions (without leading dot) supported by this toolchain"
           }
-        },
-        "additionalProperties": false
-      }
+        }
+      },
+      "additionalProperties": false
     }
-  },
-  "additionalProperties": false
+  }
 }

+ 1 - 1
Source/cmFileAPI.cxx

@@ -959,7 +959,7 @@ Json::Value cmFileAPI::BuildCMakeFiles(Object object)
 
 // The "toolchains" object kind.
 
-static unsigned int const ToolchainsV1Minor = 0;
+static unsigned int const ToolchainsV1Minor = 1;
 
 void cmFileAPI::BuildClientRequestToolchains(
   ClientRequest& r, std::vector<RequestVersion> const& versions)

+ 19 - 15
Source/cmFileAPIToolchains.cxx

@@ -24,6 +24,7 @@ struct ToolchainVariable
   std::string ObjectKey;
   std::string VariableSuffix;
   bool IsList;
+  bool OmitEmpty;
 };
 
 class Toolchains
@@ -74,22 +75,23 @@ Json::Value Toolchains::DumpToolchains()
 Json::Value Toolchains::DumpToolchain(std::string const& lang)
 {
   static std::vector<ToolchainVariable> const CompilerVariables{
-    { "path", "COMPILER", false },
-    { "id", "COMPILER_ID", false },
-    { "version", "COMPILER_VERSION", false },
-    { "target", "COMPILER_TARGET", false },
+    { "path", "COMPILER", false, false },
+    { "commandFragment", "COMPILER_ARG1", false, true },
+    { "id", "COMPILER_ID", false, false },
+    { "version", "COMPILER_VERSION", false, false },
+    { "target", "COMPILER_TARGET", false, false },
   };
 
   static std::vector<ToolchainVariable> const CompilerImplicitVariables{
-    { "includeDirectories", "IMPLICIT_INCLUDE_DIRECTORIES", true },
-    { "linkDirectories", "IMPLICIT_LINK_DIRECTORIES", true },
-    { "linkFrameworkDirectories", "IMPLICIT_LINK_FRAMEWORK_DIRECTORIES",
-      true },
-    { "linkLibraries", "IMPLICIT_LINK_LIBRARIES", true },
+    { "includeDirectories", "IMPLICIT_INCLUDE_DIRECTORIES", true, false },
+    { "linkDirectories", "IMPLICIT_LINK_DIRECTORIES", true, false },
+    { "linkFrameworkDirectories", "IMPLICIT_LINK_FRAMEWORK_DIRECTORIES", true,
+      false },
+    { "linkLibraries", "IMPLICIT_LINK_LIBRARIES", true, false },
   };
 
   static ToolchainVariable const SourceFileExtensionsVariable{
-    "sourceFileExtensions", "SOURCE_FILE_EXTENSIONS", true
+    "sourceFileExtensions", "SOURCE_FILE_EXTENSIONS", true, false
   };
 
   auto const& mf =
@@ -128,15 +130,17 @@ void Toolchains::DumpToolchainVariable(cmMakefile const* mf,
     cmValue data = mf->GetDefinition(variableName);
     if (data) {
       cmList values(data);
-      Json::Value jsonArray = Json::arrayValue;
-      for (auto const& value : values) {
-        jsonArray.append(value);
+      if (!variable.OmitEmpty || !values.empty()) {
+        Json::Value jsonArray = Json::arrayValue;
+        for (auto const& value : values) {
+          jsonArray.append(value);
+        }
+        object[variable.ObjectKey] = jsonArray;
       }
-      object[variable.ObjectKey] = jsonArray;
     }
   } else {
     cmValue def = mf->GetDefinition(variableName);
-    if (def) {
+    if (def && (!variable.OmitEmpty || !def.IsEmpty())) {
       object[variable.ObjectKey] = *def;
     }
   }

+ 1 - 1
Tests/RunCMake/CommandLine/E_capabilities-stdout.txt

@@ -1 +1 @@
-^{"debugger":(true|false),"fileApi":{"requests":\[{"kind":"codemodel","version":\[{"major":2,"minor":9}]},{"kind":"configureLog","version":\[{"major":1,"minor":0}]},{"kind":"cache","version":\[{"major":2,"minor":0}]},{"kind":"cmakeFiles","version":\[{"major":1,"minor":1}]},{"kind":"toolchains","version":\[{"major":1,"minor":0}]}]},"generators":\[.*\],"serverMode":false,"tls":(true|false),"version":{.*}}$
+^{"debugger":(true|false),"fileApi":{"requests":\[{"kind":"codemodel","version":\[{"major":2,"minor":9}]},{"kind":"configureLog","version":\[{"major":1,"minor":0}]},{"kind":"cache","version":\[{"major":2,"minor":0}]},{"kind":"cmakeFiles","version":\[{"major":1,"minor":1}]},{"kind":"toolchains","version":\[{"major":1,"minor":1}]}]},"generators":\[.*\],"serverMode":false,"tls":(true|false),"version":{.*}}$

+ 1 - 1
Tests/RunCMake/FileAPI/ProjectQueryBad.cmake

@@ -30,7 +30,7 @@ cmake_file_api(
   CODEMODEL 3
   CACHE 3
   CMAKEFILES 2
-  TOOLCHAINS 1.1
+  TOOLCHAINS 1.2
 )
 
 message(NOTICE "Requested versions too low check")

+ 3 - 1
Tests/RunCMake/FileAPI/RunCMakeTest.cmake

@@ -146,7 +146,7 @@ run_cmake(FailConfigure)
 
 function(run_object object)
   set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/${object}-build)
-  list(APPEND RunCMake_TEST_OPTIONS -DCMAKE_POLICY_DEFAULT_CMP0118=NEW)
+  list(APPEND RunCMake_TEST_OPTIONS ${ARGN} -DCMAKE_POLICY_DEFAULT_CMP0118=NEW)
   run_cmake(${object})
   list(POP_BACK RunCMake_TEST_OPTIONS)
   set(RunCMake_TEST_NO_CLEAN 1)
@@ -161,3 +161,5 @@ run_object(configureLog-v1)
 run_object(cache-v2)
 run_object(cmakeFiles-v1)
 run_object(toolchains-v1)
+run_object(toolchains-v1 -DTOOLCHAINSV1_COMPILERARGS=1)
+run_object(toolchains-v1 -DTOOLCHAINSV1_COMPILERARGS=2)

+ 16 - 7
Tests/RunCMake/FileAPI/toolchains-v1-check.py

@@ -2,17 +2,20 @@ from check_index import *
 import os
 
 class ExpectedVar(object):
-    def __init__(self, name):
+    def __init__(self, name, omitEmpty=False):
         self.name = name
+        self.omitEmpty = omitEmpty
 
 class ExpectedList(object):
-    def __init__(self, name):
+    def __init__(self, name, omitEmpty=False):
         self.name = name
+        self.omitEmpty = omitEmpty
 
 EXPECTED_TOOLCHAIN = {
     "language": "CXX",
     "compiler": {
         "path": ExpectedVar("CMAKE_CXX_COMPILER"),
+        "commandFragment": ExpectedVar("CMAKE_CXX_COMPILER_ARG1", omitEmpty=True),
         "id": ExpectedVar("CMAKE_CXX_COMPILER_ID"),
         "version": ExpectedVar("CMAKE_CXX_COMPILER_VERSION"),
         "target": ExpectedVar("CMAKE_CXX_COMPILER_TARGET"),
@@ -35,7 +38,7 @@ EXPECTED_TOOLCHAIN = {
 def check_objects(o):
     assert is_list(o)
     assert len(o) == 1
-    check_index_object(o[0], "toolchains", 1, 0, check_object_toolchains)
+    check_index_object(o[0], "toolchains", 1, 1, check_object_toolchains)
 
 def check_object_toolchains(o):
     assert sorted(o.keys()) == ["kind", "toolchains", "version"]
@@ -59,17 +62,18 @@ def check_object_toolchain(o, expected):
         key for (key, value) in expected.items()
         if is_string(value) or is_dict(value)
             or (type(value) in (ExpectedVar, ExpectedList)
-                and variables[value.name]["defined"])]
-    assert sorted(o.keys()) == sorted(expected_keys)
+                and variables[value.name]["defined"]
+                and not (value.omitEmpty and variables[value.name]["value"] == ''))]
+    assert sorted(o.keys()) == sorted(expected_keys), "actual object {!r}, expected keys {!r}".format(o, sorted(expected_keys))
 
     for key in expected_keys:
         value = expected[key]
         if is_string(value):
-            assert o[key] == value
+            assert o[key] == value, "{!r}: actual {!r}, expected {!r}".format(key, o[key], value)
         elif is_dict(value):
             check_object_toolchain(o[key], value)
         elif type(value) == ExpectedVar:
-            assert o[key] == variables[value.name]["value"]
+            assert o[key] == variables[value.name]["value"], "{!r}: actual {!r}, expected {!r} (from {})".format(key, o[key], variables[value.name]["value"], value.name)
         elif type(value) == ExpectedList:
             expected_items = filter(
                 None, variables[value.name]["value"].split(";"))
@@ -81,6 +85,11 @@ with open(os.path.join(args.build_dir, "toolchain_variables.json")) as f:
     variables = json.load(f)
 
 assert is_dict(variables)
+if variables.get("TOOLCHAINSV1_COMPILERARGS", 0) == 1:
+    del EXPECTED_TOOLCHAIN["compiler"]["commandFragment"]
+elif variables.get("TOOLCHAINSV1_COMPILERARGS", 0) == 2:
+    EXPECTED_TOOLCHAIN["compiler"]["commandFragment"] = "--hello world --something=other"
+
 assert is_dict(index)
 assert sorted(index.keys()) == ["cmake", "objects", "reply"]
 check_objects(index["objects"])

+ 17 - 1
Tests/RunCMake/FileAPI/toolchains-v1.cmake

@@ -1,7 +1,22 @@
+# If testing with a specific CMAKE_CXX_COMPILER_ARG1 value is requested, skip
+# any checks that try to actually compile anything, because the compiler
+# probably wouldn't understand these arguments or lack thereof.
+if(DEFINED TOOLCHAINSV1_COMPILERARGS)
+  if(TOOLCHAINSV1_COMPILERARGS EQUAL 1)
+    set(CMAKE_CXX_COMPILER_ARG1 "")
+  elseif(TOOLCHAINSV1_COMPILERARGS EQUAL 2)
+    set(CMAKE_CXX_COMPILER_ARG1 "--hello world --something=other")
+  endif()
+  set(CMAKE_CXX_COMPILER_WORKS 1)
+  set(CMAKE_CXX_ABI_COMPILED 1)
+else()
+  set(TOOLCHAINSV1_COMPILERARGS 0)
+endif()
+
 enable_language(CXX)
 
 set(variable_suffixes
-  COMPILER COMPILER_ID COMPILER_VERSION COMPILER_TARGET
+  COMPILER COMPILER_ARG1 COMPILER_ID COMPILER_VERSION COMPILER_TARGET
   IMPLICIT_INCLUDE_DIRECTORIES IMPLICIT_LINK_DIRECTORIES
   IMPLICIT_LINK_FRAMEWORK_DIRECTORIES IMPLICIT_LINK_LIBRARIES
   SOURCE_FILE_EXTENSIONS)
@@ -18,6 +33,7 @@ foreach(variable_suffix ${variable_suffixes})
     string(JSON json SET "${json}" "${variable}" "defined" "false")
   endif()
 endforeach()
+string(JSON json SET "${json}" "TOOLCHAINSV1_COMPILERARGS" "${TOOLCHAINSV1_COMPILERARGS}")
 
 file(WRITE ${CMAKE_BINARY_DIR}/toolchain_variables.json "${json}")