aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJussi Pakkanen <jpakkane@gmail.com>2019-07-31 18:40:15 +0300
committerGitHub <noreply@github.com>2019-07-31 18:40:15 +0300
commit679ddb0ae780bfd4d81c586b0512255c0d1e24b6 (patch)
treee5f472bb7a82f1f36c45969e9dd2aad65f7d832a
parent5d9a1558c2e1041b1149fca6b2bceba447b6823e (diff)
parent497fbf0ce096513957bf4eccd08a2cc942730b0c (diff)
downloadmeson-679ddb0ae780bfd4d81c586b0512255c0d1e24b6.zip
meson-679ddb0ae780bfd4d81c586b0512255c0d1e24b6.tar.gz
meson-679ddb0ae780bfd4d81c586b0512255c0d1e24b6.tar.bz2
Merge pull request #5638 from mensinda/cmInterface
CMake: Support INTERFACE libraries
-rw-r--r--mesonbuild/cmake/interpreter.py44
-rw-r--r--mesonbuild/cmake/traceparser.py96
-rw-r--r--test cases/cmake/9 header only/main.cpp10
-rw-r--r--test cases/cmake/9 header only/meson.build12
-rw-r--r--test cases/cmake/9 header only/subprojects/cmMod/CMakeLists.txt11
-rw-r--r--test cases/cmake/9 header only/subprojects/cmMod/include/cmMod.hpp19
6 files changed, 173 insertions, 19 deletions
diff --git a/mesonbuild/cmake/interpreter.py b/mesonbuild/cmake/interpreter.py
index 28a8488..5687ad2 100644
--- a/mesonbuild/cmake/interpreter.py
+++ b/mesonbuild/cmake/interpreter.py
@@ -74,8 +74,11 @@ target_type_map = {
'SHARED_LIBRARY': 'shared_library',
'EXECUTABLE': 'executable',
'OBJECT_LIBRARY': 'static_library',
+ 'INTERFACE_LIBRARY': 'header_only'
}
+target_type_requires_trace = ['INTERFACE_LIBRARY']
+
skip_targets = ['UTILITY']
blacklist_compiler_flags = [
@@ -138,6 +141,7 @@ class ConverterTarget:
self.link_with = []
self.object_libs = []
self.compile_opts = {}
+ self.public_compile_opts = []
self.pie = False
# Project default override options (c_std, cpp_std, etc.)
@@ -170,7 +174,7 @@ class ConverterTarget:
std_regex = re.compile(r'([-]{1,2}std=|/std:v?|[-]{1,2}std:)(.*)')
- def postprocess(self, output_target_map: dict, root_src_dir: str, subdir: str, install_prefix: str) -> None:
+ def postprocess(self, output_target_map: dict, root_src_dir: str, subdir: str, install_prefix: str, trace: CMakeTraceParser) -> None:
# Detect setting the C and C++ standard
for i in ['c', 'cpp']:
if i not in self.compile_opts:
@@ -194,6 +198,18 @@ class ConverterTarget:
if self.type.upper() == 'OBJECT_LIBRARY':
self.pie = True
+ # Use the CMake trace, if required
+ if self.type.upper() in target_type_requires_trace:
+ if self.name in trace.targets:
+ props = trace.targets[self.name].properies
+
+ self.includes += props.get('INTERFACE_INCLUDE_DIRECTORIES', [])
+ self.public_compile_opts += props.get('INTERFACE_COMPILE_DEFINITIONS', [])
+ self.public_compile_opts += props.get('INTERFACE_COMPILE_OPTIONS', [])
+ self.link_flags += props.get('INTERFACE_LINK_OPTIONS', [])
+ else:
+ mlog.warning('CMake: Target', mlog.bold(self.name), 'not found in CMake trace. This can lead to build errors')
+
# Fix link libraries
temp = []
for i in self.link_libraries:
@@ -584,7 +600,7 @@ class CMakeInterpreter:
for i in self.custom_targets:
i.postprocess(output_target_map, self.src_dir, self.subdir, self.build_dir)
for i in self.targets:
- i.postprocess(output_target_map, self.src_dir, self.subdir, self.install_prefix)
+ i.postprocess(output_target_map, self.src_dir, self.subdir, self.install_prefix, self.trace)
if i.type == 'OBJECT_LIBRARY':
object_libs += [i]
self.languages += [x for x in i.languages if x not in self.languages]
@@ -762,17 +778,31 @@ class CMakeInterpreter:
dep_kwargs = {
'link_args': tgt.link_flags + tgt.link_libraries,
'link_with': id_node(tgt_var),
+ 'compile_args': tgt.public_compile_opts,
'include_directories': id_node(inc_var),
}
# Generate the function nodes
- inc_node = assign(inc_var, function('include_directories', tgt.includes))
- src_node = assign(src_var, function('files', sources))
- tgt_node = assign(tgt_var, function(tgt_func, [base_name, [id_node(src_var)] + generated], tgt_kwargs))
- dep_node = assign(dep_var, function('declare_dependency', kwargs=dep_kwargs))
+ node_list = []
+ if tgt_func == 'header_only':
+ del dep_kwargs['link_with']
+ inc_node = assign(inc_var, function('include_directories', tgt.includes))
+ dep_node = assign(dep_var, function('declare_dependency', kwargs=dep_kwargs))
+
+ node_list = [inc_node, dep_node]
+ src_var = ''
+ tgt_var = ''
+
+ else:
+ inc_node = assign(inc_var, function('include_directories', tgt.includes))
+ src_node = assign(src_var, function('files', sources))
+ tgt_node = assign(tgt_var, function(tgt_func, [base_name, [id_node(src_var)] + generated], tgt_kwargs))
+ dep_node = assign(dep_var, function('declare_dependency', kwargs=dep_kwargs))
+
+ node_list = [inc_node, src_node, tgt_node, dep_node]
# Add the nodes to the ast
- root_cb.lines += [inc_node, src_node, tgt_node, dep_node]
+ root_cb.lines += node_list
processed[tgt.name] = {'inc': inc_var, 'src': src_var, 'dep': dep_var, 'tgt': tgt_var, 'func': tgt_func}
def process_custom_target(tgt: ConverterCustomTarget) -> None:
diff --git a/mesonbuild/cmake/traceparser.py b/mesonbuild/cmake/traceparser.py
index 4b87319..6106d16 100644
--- a/mesonbuild/cmake/traceparser.py
+++ b/mesonbuild/cmake/traceparser.py
@@ -81,7 +81,11 @@ class CMakeTraceParser:
'add_custom_command': self._cmake_add_custom_command,
'add_custom_target': self._cmake_add_custom_target,
'set_property': self._cmake_set_property,
- 'set_target_properties': self._cmake_set_target_properties
+ 'set_target_properties': self._cmake_set_target_properties,
+ 'target_compile_definitions': self._cmake_target_compile_definitions,
+ 'target_compile_options': self._cmake_target_compile_options,
+ 'target_include_directories': self._cmake_target_include_directories,
+ 'target_link_options': self._cmake_target_link_options,
}
# Primary pass -- parse everything
@@ -199,16 +203,23 @@ class CMakeTraceParser:
args = list(tline.args) # Make a working copy
# Make sure the lib is imported
- if 'IMPORTED' not in args:
- return self._gen_exception('add_library', 'non imported libraries are not supported', tline)
+ if 'INTERFACE' in args:
+ args.remove('INTERFACE')
- args.remove('IMPORTED')
+ if len(args) < 1:
+ return self._gen_exception('add_library', 'interface library name not specified', tline)
- # No only look at the first two arguments (target_name and target_type) and ignore the rest
- if len(args) < 2:
- return self._gen_exception('add_library', 'requires at least 2 arguments', tline)
+ self.targets[args[0]] = CMakeTarget(args[0], 'INTERFACE', {})
+ elif 'IMPORTED' in args:
+ args.remove('IMPORTED')
- self.targets[args[0]] = CMakeTarget(args[0], args[1], {})
+ # No only look at the first two arguments (target_name and target_type) and ignore the rest
+ if len(args) < 2:
+ return self._gen_exception('add_library', 'requires at least 2 arguments', tline)
+
+ self.targets[args[0]] = CMakeTarget(args[0], args[1], {})
+ else:
+ return self._gen_exception('add_library', 'non imported / interface libraries are not supported', tline)
def _cmake_add_custom_command(self, tline: CMakeTraceLine):
# DOC: https://cmake.org/cmake/help/latest/command/add_custom_command.html
@@ -343,8 +354,8 @@ class CMakeTraceParser:
# set_property() this is not context free. There are two approaches I
# can think of, both have drawbacks:
#
- # 1. Assume that the property will be capitalized, this is convention
- # but cmake doesn't require it.
+ # 1. Assume that the property will be capitalized ([A-Z_]), this is
+ # convention but cmake doesn't require it.
# 2. Maintain a copy of the list here: https://cmake.org/cmake/help/latest/manual/cmake-properties.7.html#target-properties
#
# Neither of these is awesome for obvious reasons. I'm going to try
@@ -354,8 +365,9 @@ class CMakeTraceParser:
arglist = [] # type: List[Tuple[str, List[str]]]
name = args.pop(0)
values = []
+ prop_regex = re.compile(r'^[A-Z_]+$')
for a in args:
- if a.isupper():
+ if prop_regex.match(a):
if values:
arglist.append((name, ' '.join(values).split(';')))
name = a
@@ -372,6 +384,66 @@ class CMakeTraceParser:
self.targets[i].properies[name] = value
+ def _cmake_target_compile_definitions(self, tline: CMakeTraceLine) -> None:
+ # DOC: https://cmake.org/cmake/help/latest/command/target_compile_definitions.html
+ self._parse_common_target_options('target_compile_definitions', 'COMPILE_DEFINITIONS', 'INTERFACE_COMPILE_DEFINITIONS', tline)
+
+ def _cmake_target_compile_options(self, tline: CMakeTraceLine) -> None:
+ # DOC: https://cmake.org/cmake/help/latest/command/target_compile_options.html
+ self._parse_common_target_options('target_compile_options', 'COMPILE_OPTIONS', 'INTERFACE_COMPILE_OPTIONS', tline)
+
+ def _cmake_target_include_directories(self, tline: CMakeTraceLine) -> None:
+ # DOC: https://cmake.org/cmake/help/latest/command/target_include_directories.html
+ self._parse_common_target_options('target_include_directories', 'INCLUDE_DIRECTORIES', 'INTERFACE_INCLUDE_DIRECTORIES', tline, ignore=['SYSTEM', 'BEFORE'], paths=True)
+
+ def _cmake_target_link_options(self, tline: CMakeTraceLine) -> None:
+ # DOC: https://cmake.org/cmake/help/latest/command/target_link_options.html
+ self._parse_common_target_options('target_link_options', 'LINK_OPTIONS', 'INTERFACE_LINK_OPTIONS', tline)
+
+ def _parse_common_target_options(self, func: str, private_prop: str, interface_prop: str, tline: CMakeTraceLine, ignore: Optional[List[str]] = None, paths: bool = False):
+ if ignore is None:
+ ignore = ['BEFORE']
+
+ args = list(tline.args)
+
+ if len(args) < 1:
+ return self._gen_exception(func, 'requires at least one argument', tline)
+
+ target = args[0]
+ if target not in self.targets:
+ return self._gen_exception(func, 'TARGET {} not found'.format(target), tline)
+
+ interface = []
+ private = []
+
+ mode = 'PUBLIC'
+ for i in args[1:]:
+ if i in ignore:
+ continue
+
+ if i in ['INTERFACE', 'PUBLIC', 'PRIVATE']:
+ mode = i
+ continue
+
+ if mode in ['INTERFACE', 'PUBLIC']:
+ interface += [i]
+
+ if mode in ['PUBLIC', 'PRIVATE']:
+ private += [i]
+
+ if paths:
+ interface = self._guess_files(interface)
+ private = self._guess_files(private)
+
+ interface = [x for x in interface if x]
+ private = [x for x in private if x]
+
+ for i in [(private_prop, private), (interface_prop, interface)]:
+ if not i[0] in self.targets[target].properies:
+ self.targets[target].properies[i[0]] = []
+
+ self.targets[target].properies[i[0]] += i[1]
+
def _lex_trace(self, trace):
# The trace format is: '<file>(<line>): <func>(<args -- can contain \n> )\n'
reg_tline = re.compile(r'\s*(.*\.(cmake|txt))\(([0-9]+)\):\s*(\w+)\(([\s\S]*?) ?\)\s*\n', re.MULTILINE)
@@ -420,7 +492,7 @@ class CMakeTraceParser:
# Abort concatination if curr_str no longer matches the regex
fixed_list += [curr_str]
curr_str = i
- elif reg_end.match(i):
+ elif reg_end.match(i) or os.path.exists('{} {}'.format(curr_str, i)):
# File detected
curr_str = '{} {}'.format(curr_str, i)
fixed_list += [curr_str]
diff --git a/test cases/cmake/9 header only/main.cpp b/test cases/cmake/9 header only/main.cpp
new file mode 100644
index 0000000..315c0f7
--- /dev/null
+++ b/test cases/cmake/9 header only/main.cpp
@@ -0,0 +1,10 @@
+#include <iostream>
+#include <cmMod.hpp>
+
+using namespace std;
+
+int main() {
+ cmModClass obj("Hello");
+ cout << obj.getStr() << endl;
+ return 0;
+}
diff --git a/test cases/cmake/9 header only/meson.build b/test cases/cmake/9 header only/meson.build
new file mode 100644
index 0000000..ca08a3f
--- /dev/null
+++ b/test cases/cmake/9 header only/meson.build
@@ -0,0 +1,12 @@
+project('cmakeSubTest', ['c', 'cpp'])
+
+cm = import('cmake')
+
+sub_pro = cm.subproject('cmMod')
+sub_dep = sub_pro.dependency('cmModLib')
+
+assert(sub_pro.target_list() == ['cmModLib'], 'There should be exactly one target')
+assert(sub_pro.target_type('cmModLib') == 'header_only', 'Target type should be header_only')
+
+exe1 = executable('main', ['main.cpp'], dependencies: [sub_dep])
+test('test1', exe1)
diff --git a/test cases/cmake/9 header only/subprojects/cmMod/CMakeLists.txt b/test cases/cmake/9 header only/subprojects/cmMod/CMakeLists.txt
new file mode 100644
index 0000000..f5d9a47
--- /dev/null
+++ b/test cases/cmake/9 header only/subprojects/cmMod/CMakeLists.txt
@@ -0,0 +1,11 @@
+cmake_minimum_required(VERSION 3.5)
+
+project(cmMod)
+set (CMAKE_CXX_STANDARD 14)
+
+add_definitions("-DDO_NOTHING_JUST_A_FLAG=1")
+
+add_library(cmModLib INTERFACE)
+set_target_properties(cmModLib PROPERTIES INTERFACE_COMPILE_OPTIONS "-DCMAKE_FLAG_MUST_BE_PRESENT")
+target_include_directories(cmModLib INTERFACE "${CMAKE_CURRENT_SOURCE_DIR}" "${CMAKE_CURRENT_SOURCE_DIR}/include")
+target_compile_definitions(cmModLib INTERFACE -DCMAKE_COMPILER_DEFINE_STR="compDef")
diff --git a/test cases/cmake/9 header only/subprojects/cmMod/include/cmMod.hpp b/test cases/cmake/9 header only/subprojects/cmMod/include/cmMod.hpp
new file mode 100644
index 0000000..7ea72f7
--- /dev/null
+++ b/test cases/cmake/9 header only/subprojects/cmMod/include/cmMod.hpp
@@ -0,0 +1,19 @@
+#pragma once
+
+#include <string>
+
+#ifndef CMAKE_FLAG_MUST_BE_PRESENT
+#error "The flag CMAKE_FLAG_MUST_BE_PRESENT was not set"
+#endif
+
+class cmModClass {
+ private:
+ std::string str;
+ public:
+ cmModClass(std::string foo) {
+ str = foo + " World ";
+ str += CMAKE_COMPILER_DEFINE_STR;
+ }
+
+ inline std::string getStr() const { return str; }
+};