aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaniel Mensinger <daniel@mensinger-ka.de>2019-12-18 15:32:13 +0100
committerJussi Pakkanen <jpakkane@gmail.com>2019-12-22 22:38:20 +0200
commit77e0008a1fede82851fdf13438619cb62e61c297 (patch)
treec17db0ec3c718b5d664534344056b5139ae68fa4
parent7981308e6e600535efeba6b50cdda62d43e7c584 (diff)
downloadmeson-77e0008a1fede82851fdf13438619cb62e61c297.zip
meson-77e0008a1fede82851fdf13438619cb62e61c297.tar.gz
meson-77e0008a1fede82851fdf13438619cb62e61c297.tar.bz2
cmake: Fix obeject libraries
This fixes an issue with generated sources and object libraries, as well as an issue on windows with the `link` linker and the vs backend. The last issue is resolved by building the source files multiple times to avoid extracting object files in meson.
-rw-r--r--docs/markdown/snippets/cmake_improvements.md11
-rw-r--r--mesonbuild/cmake/interpreter.py64
-rw-r--r--test cases/cmake/15 object library advanced/main.cpp11
-rw-r--r--test cases/cmake/15 object library advanced/meson.build13
-rw-r--r--test cases/cmake/15 object library advanced/subprojects/cmObjLib/CMakeLists.txt18
-rw-r--r--test cases/cmake/15 object library advanced/subprojects/cmObjLib/genC.cpp31
-rw-r--r--test cases/cmake/15 object library advanced/subprojects/cmObjLib/libA.cpp9
-rw-r--r--test cases/cmake/15 object library advanced/subprojects/cmObjLib/libA.hpp16
-rw-r--r--test cases/cmake/15 object library advanced/subprojects/cmObjLib/libB.cpp6
-rw-r--r--test cases/cmake/15 object library advanced/subprojects/cmObjLib/libB.hpp16
-rw-r--r--test cases/cmake/5 object library/meson.build7
-rw-r--r--test cases/cmake/5 object library/subprojects/cmObjLib/libA.hpp13
-rw-r--r--test cases/cmake/5 object library/subprojects/cmObjLib/libB.hpp13
-rw-r--r--test cases/cmake/6 object library no dep/meson.build5
-rw-r--r--test cases/cmake/6 object library no dep/subprojects/cmObjLib/libA.hpp13
-rw-r--r--test cases/cmake/6 object library no dep/subprojects/cmObjLib/libB.hpp13
16 files changed, 236 insertions, 23 deletions
diff --git a/docs/markdown/snippets/cmake_improvements.md b/docs/markdown/snippets/cmake_improvements.md
new file mode 100644
index 0000000..95f30cc
--- /dev/null
+++ b/docs/markdown/snippets/cmake_improvements.md
@@ -0,0 +1,11 @@
+## Improved CMake subprojects support
+
+With this release even more CMake projects are supported via
+[CMake subprojects](CMake-module.md#cmake-subprojects) due to these internal
+improvements:
+
+- Use the CMake file API for CMake >=3.14
+- Handle the explicit dependencies via `add_dependency`
+- Basic support for `add_custom_target`
+- Improved `add_custom_command` support
+- Object library support on Windows
diff --git a/mesonbuild/cmake/interpreter.py b/mesonbuild/cmake/interpreter.py
index d9f1e18..66d0994 100644
--- a/mesonbuild/cmake/interpreter.py
+++ b/mesonbuild/cmake/interpreter.py
@@ -25,9 +25,10 @@ from ..environment import Environment
from ..mesonlib import MachineChoice, version_compare
from ..compilers.compilers import lang_suffixes, header_suffixes, obj_suffixes, lib_suffixes, is_header
from subprocess import Popen, PIPE
-from typing import Any, List, Dict, Optional, Union, TYPE_CHECKING
+from typing import Any, List, Dict, Optional, Set, Union, TYPE_CHECKING
from threading import Thread
from enum import Enum
+from functools import lru_cache
import os, re
from ..mparser import (
@@ -391,23 +392,60 @@ class ConverterTarget:
if tgt:
self.depends.append(tgt)
- def process_object_libs(self, obj_target_list: List['ConverterTarget']):
+ def process_object_libs(self, obj_target_list: List['ConverterTarget'], linker_workaround: bool):
# Try to detect the object library(s) from the generated input sources
temp = [x for x in self.generated if isinstance(x, str)]
temp = [os.path.basename(x) for x in temp]
temp = [x for x in temp if any([x.endswith('.' + y) for y in obj_suffixes])]
temp = [os.path.splitext(x)[0] for x in temp]
+ exts = self._all_source_suffixes()
# Temp now stores the source filenames of the object files
for i in obj_target_list:
- source_files = [os.path.basename(x) for x in i.sources + i.generated]
- for j in source_files:
- if j in temp:
- self.object_libs += [i]
+ source_files = [x for x in i.sources + i.generated if isinstance(x, str)]
+ source_files = [os.path.basename(x) for x in source_files]
+ for j in temp:
+ # On some platforms (specifically looking at you Windows with vs20xy backend) CMake does
+ # not produce object files with the format `foo.cpp.obj`, instead it skipps the language
+ # suffix and just produces object files like `foo.obj`. Thus we have to do our best to
+ # undo this step and guess the correct language suffix of the object file. This is done
+ # by trying all language suffixes meson knows and checking if one of them fits.
+ candidates = [j] # type: List[str]
+ if not any([j.endswith('.' + x) for x in exts]):
+ mlog.warning('Object files do not contain source file extensions, thus falling back to guessing them.', once=True)
+ candidates += ['{}.{}'.format(j, x) for x in exts]
+ if any([x in source_files for x in candidates]):
+ if linker_workaround:
+ self._append_objlib_sources(i)
+ else:
+ self.includes += i.includes
+ self.includes = list(set(self.includes))
+ self.object_libs += [i]
break
# Filter out object files from the sources
self.generated = [x for x in self.generated if not isinstance(x, str) or not any([x.endswith('.' + y) for y in obj_suffixes])]
+ def _append_objlib_sources(self, tgt: 'ConverterTarget') -> None:
+ self.includes += tgt.includes
+ self.sources += tgt.sources
+ self.generated += tgt.generated
+ self.sources = list(set(self.sources))
+ self.generated = list(set(self.generated))
+ self.includes = list(set(self.includes))
+
+ # Inherit compiler arguments since they may be required for building
+ for lang, opts in tgt.compile_opts.items():
+ if lang not in self.compile_opts:
+ self.compile_opts[lang] = []
+ self.compile_opts[lang] += [x for x in opts if x not in self.compile_opts[lang]]
+
+ @lru_cache(maxsize=None)
+ def _all_source_suffixes(self) -> List[str]:
+ suffixes = [] # type: List[str]
+ for exts in lang_suffixes.values():
+ suffixes += [x for x in exts]
+ return suffixes
+
def process_inter_target_dependencies(self):
# Move the dependencies from all transfer_dependencies_from to the target
to_process = list(self.depends)
@@ -620,6 +658,7 @@ class CMakeInterpreter:
self.install_prefix = install_prefix
self.env = env
self.backend_name = backend.name
+ self.linkers = set() # type: Set[str]
self.cmake_api = CMakeAPI.SERVER
self.client = CMakeClient(self.env)
self.fileapi = CMakeFileAPI(self.build_dir)
@@ -661,6 +700,7 @@ class CMakeInterpreter:
for lang, comp in self.env.coredata.compilers[for_machine].items():
if lang not in language_map:
continue
+ self.linkers.add(comp.get_linker_id())
cmake_lang = language_map[lang]
exelist = comp.get_exelist()
if len(exelist) == 1:
@@ -787,12 +827,16 @@ class CMakeInterpreter:
self.trace.parse(self.raw_trace)
# Find all targets
+ added_target_names = [] # type: List[str]
for i in self.codemodel_configs:
for j in i.projects:
if not self.project_name:
self.project_name = j.name
for k in j.targets:
- if k.type not in skip_targets:
+ # Avoid duplicate targets from different configurations and known
+ # dummy CMake internal target types
+ if k.type not in skip_targets and k.name not in added_target_names:
+ added_target_names += [k.name]
self.targets += [ConverterTarget(k, self.env)]
# Add interface targets from trace, if not already present.
@@ -830,7 +874,7 @@ class CMakeInterpreter:
# Second pass: Detect object library dependencies
for i in self.targets:
- i.process_object_libs(object_libs)
+ i.process_object_libs(object_libs, self._object_lib_workaround())
# Third pass: Reassign dependencies to avoid some loops
for i in self.targets:
@@ -1010,6 +1054,7 @@ class CMakeInterpreter:
# Generate target kwargs
tgt_kwargs = {
+ 'build_by_default': False,
'link_args': tgt.link_flags + tgt.link_libraries,
'link_with': link_with,
'include_directories': id_node(inc_var),
@@ -1144,3 +1189,6 @@ class CMakeInterpreter:
res = [x for x in self.generated_targets.keys()]
res = [x[prx_len:] if x.startswith(prx_str) else x for x in res]
return res
+
+ def _object_lib_workaround(self) -> bool:
+ return 'link' in self.linkers and self.backend_name.startswith('vs')
diff --git a/test cases/cmake/15 object library advanced/main.cpp b/test cases/cmake/15 object library advanced/main.cpp
new file mode 100644
index 0000000..4cc01a8
--- /dev/null
+++ b/test cases/cmake/15 object library advanced/main.cpp
@@ -0,0 +1,11 @@
+#include <iostream>
+#include "libA.hpp"
+#include "libB.hpp"
+
+using namespace std;
+
+int main(void) {
+ cout << getLibStr() << endl;
+ cout << getZlibVers() << endl;
+ return EXIT_SUCCESS;
+}
diff --git a/test cases/cmake/15 object library advanced/meson.build b/test cases/cmake/15 object library advanced/meson.build
new file mode 100644
index 0000000..6a4448b
--- /dev/null
+++ b/test cases/cmake/15 object library advanced/meson.build
@@ -0,0 +1,13 @@
+project('cmake_object_lib_test', 'cpp', default_options: ['cpp_std=c++11'])
+
+cm = import('cmake')
+
+sub_pro = cm.subproject('cmObjLib')
+sub_sha = sub_pro.dependency('lib_sha')
+sub_sta = sub_pro.dependency('lib_sta')
+
+exe_sha = executable('shared', ['main.cpp'], dependencies: [sub_sha])
+exe_sta = executable('static', ['main.cpp'], dependencies: [sub_sta])
+
+test('test1', exe_sha)
+test('test1', exe_sta)
diff --git a/test cases/cmake/15 object library advanced/subprojects/cmObjLib/CMakeLists.txt b/test cases/cmake/15 object library advanced/subprojects/cmObjLib/CMakeLists.txt
new file mode 100644
index 0000000..47f1ad3
--- /dev/null
+++ b/test cases/cmake/15 object library advanced/subprojects/cmObjLib/CMakeLists.txt
@@ -0,0 +1,18 @@
+cmake_minimum_required(VERSION 3.7)
+project(cmObject CXX)
+
+add_executable(genC genC.cpp)
+
+add_custom_command(
+ OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/libC.cpp" "${CMAKE_CURRENT_BINARY_DIR}/libC.hpp"
+ COMMAND genC
+ WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}"
+)
+
+include_directories("${CMAKE_CURRENT_BINARY_DIR}")
+
+add_library(lib_obj OBJECT libA.cpp libB.cpp "${CMAKE_CURRENT_BINARY_DIR}/libC.cpp" "${CMAKE_CURRENT_BINARY_DIR}/libC.hpp")
+add_library(lib_sha SHARED $<TARGET_OBJECTS:lib_obj>)
+add_library(lib_sta STATIC $<TARGET_OBJECTS:lib_obj>)
+
+target_compile_definitions(lib_obj PRIVATE "-DBUILD_AS_OBJ=1")
diff --git a/test cases/cmake/15 object library advanced/subprojects/cmObjLib/genC.cpp b/test cases/cmake/15 object library advanced/subprojects/cmObjLib/genC.cpp
new file mode 100644
index 0000000..a9e4b5e
--- /dev/null
+++ b/test cases/cmake/15 object library advanced/subprojects/cmObjLib/genC.cpp
@@ -0,0 +1,31 @@
+#include <iostream>
+#include <fstream>
+
+using namespace std;
+
+int main() {
+ ofstream hpp("libC.hpp");
+ ofstream cpp("libC.cpp");
+ if (!hpp.is_open() || !cpp.is_open()) {
+ cerr << "Failed to open 'libC.hpp' or 'libC.cpp' for writing" << endl;
+ return 1;
+ }
+
+ hpp << R"cpp(
+#pragma once
+
+#include <string>
+
+std::string getGenStr();
+)cpp";
+
+ cpp << R"cpp(
+#include "libC.hpp"
+
+std::string getGenStr(void) {
+ return "GEN STR";
+}
+)cpp";
+
+ return 0;
+} \ No newline at end of file
diff --git a/test cases/cmake/15 object library advanced/subprojects/cmObjLib/libA.cpp b/test cases/cmake/15 object library advanced/subprojects/cmObjLib/libA.cpp
new file mode 100644
index 0000000..fd5aa48
--- /dev/null
+++ b/test cases/cmake/15 object library advanced/subprojects/cmObjLib/libA.cpp
@@ -0,0 +1,9 @@
+#include "libA.hpp"
+
+#if not BUILD_AS_OBJ
+#error "BUILD_AS_OBJ was not defined"
+#endif
+
+std::string getLibStr(void) {
+ return "Hello World";
+}
diff --git a/test cases/cmake/15 object library advanced/subprojects/cmObjLib/libA.hpp b/test cases/cmake/15 object library advanced/subprojects/cmObjLib/libA.hpp
new file mode 100644
index 0000000..84b7bc7
--- /dev/null
+++ b/test cases/cmake/15 object library advanced/subprojects/cmObjLib/libA.hpp
@@ -0,0 +1,16 @@
+#pragma once
+
+#include <string>
+
+#if defined _WIN32 || defined __CYGWIN__
+ #define DLL_PUBLIC __declspec(dllexport)
+#else
+ #if defined __GNUC__
+ #define DLL_PUBLIC __attribute__ ((visibility("default")))
+ #else
+ #pragma message ("Compiler does not support symbol visibility.")
+ #define DLL_PUBLIC
+ #endif
+#endif
+
+std::string DLL_PUBLIC getLibStr();
diff --git a/test cases/cmake/15 object library advanced/subprojects/cmObjLib/libB.cpp b/test cases/cmake/15 object library advanced/subprojects/cmObjLib/libB.cpp
new file mode 100644
index 0000000..4b832ec
--- /dev/null
+++ b/test cases/cmake/15 object library advanced/subprojects/cmObjLib/libB.cpp
@@ -0,0 +1,6 @@
+#include "libB.hpp"
+#include "libC.hpp"
+
+std::string getZlibVers(void) {
+ return getGenStr();
+}
diff --git a/test cases/cmake/15 object library advanced/subprojects/cmObjLib/libB.hpp b/test cases/cmake/15 object library advanced/subprojects/cmObjLib/libB.hpp
new file mode 100644
index 0000000..52ccc16
--- /dev/null
+++ b/test cases/cmake/15 object library advanced/subprojects/cmObjLib/libB.hpp
@@ -0,0 +1,16 @@
+#pragma once
+
+#include <string>
+
+#if defined _WIN32 || defined __CYGWIN__
+ #define DLL_PUBLIC __declspec(dllexport)
+#else
+ #if defined __GNUC__
+ #define DLL_PUBLIC __attribute__ ((visibility("default")))
+ #else
+ #pragma message ("Compiler does not support symbol visibility.")
+ #define DLL_PUBLIC
+ #endif
+#endif
+
+std::string DLL_PUBLIC getZlibVers();
diff --git a/test cases/cmake/5 object library/meson.build b/test cases/cmake/5 object library/meson.build
index c338136..f38a2dd 100644
--- a/test cases/cmake/5 object library/meson.build
+++ b/test cases/cmake/5 object library/meson.build
@@ -1,15 +1,10 @@
-project('cmake_object_lib_test', 'cpp')
+project('cmake_object_lib_test', ['c', 'cpp'])
dep_test = dependency('ZLIB', method: 'cmake', required: false)
if not dep_test.found()
error('MESON_SKIP_TEST: zlib is not installed')
endif
-cpp = meson.get_compiler('cpp')
-if build_machine.system() == 'windows' and cpp.get_id() != 'gcc'
- error('MESON_SKIP_TEST: Windows link.exe is not supported because of symbol export problems')
-endif
-
cm = import('cmake')
sub_pro = cm.subproject('cmObjLib')
diff --git a/test cases/cmake/5 object library/subprojects/cmObjLib/libA.hpp b/test cases/cmake/5 object library/subprojects/cmObjLib/libA.hpp
index 58c9413..84b7bc7 100644
--- a/test cases/cmake/5 object library/subprojects/cmObjLib/libA.hpp
+++ b/test cases/cmake/5 object library/subprojects/cmObjLib/libA.hpp
@@ -2,4 +2,15 @@
#include <string>
-std::string getLibStr();
+#if defined _WIN32 || defined __CYGWIN__
+ #define DLL_PUBLIC __declspec(dllexport)
+#else
+ #if defined __GNUC__
+ #define DLL_PUBLIC __attribute__ ((visibility("default")))
+ #else
+ #pragma message ("Compiler does not support symbol visibility.")
+ #define DLL_PUBLIC
+ #endif
+#endif
+
+std::string DLL_PUBLIC getLibStr();
diff --git a/test cases/cmake/5 object library/subprojects/cmObjLib/libB.hpp b/test cases/cmake/5 object library/subprojects/cmObjLib/libB.hpp
index 71db6b7..52ccc16 100644
--- a/test cases/cmake/5 object library/subprojects/cmObjLib/libB.hpp
+++ b/test cases/cmake/5 object library/subprojects/cmObjLib/libB.hpp
@@ -2,4 +2,15 @@
#include <string>
-std::string getZlibVers();
+#if defined _WIN32 || defined __CYGWIN__
+ #define DLL_PUBLIC __declspec(dllexport)
+#else
+ #if defined __GNUC__
+ #define DLL_PUBLIC __attribute__ ((visibility("default")))
+ #else
+ #pragma message ("Compiler does not support symbol visibility.")
+ #define DLL_PUBLIC
+ #endif
+#endif
+
+std::string DLL_PUBLIC getZlibVers();
diff --git a/test cases/cmake/6 object library no dep/meson.build b/test cases/cmake/6 object library no dep/meson.build
index b2c66ed..65b8700 100644
--- a/test cases/cmake/6 object library no dep/meson.build
+++ b/test cases/cmake/6 object library no dep/meson.build
@@ -1,10 +1,5 @@
project('cmake_object_lib_test', 'cpp')
-cpp = meson.get_compiler('cpp')
-if build_machine.system() == 'windows' and cpp.get_id() != 'gcc'
- error('MESON_SKIP_TEST: Windows link.exe is not supported because of symbol export problems')
-endif
-
cm = import('cmake')
sub_pro = cm.subproject('cmObjLib')
diff --git a/test cases/cmake/6 object library no dep/subprojects/cmObjLib/libA.hpp b/test cases/cmake/6 object library no dep/subprojects/cmObjLib/libA.hpp
index 58c9413..84b7bc7 100644
--- a/test cases/cmake/6 object library no dep/subprojects/cmObjLib/libA.hpp
+++ b/test cases/cmake/6 object library no dep/subprojects/cmObjLib/libA.hpp
@@ -2,4 +2,15 @@
#include <string>
-std::string getLibStr();
+#if defined _WIN32 || defined __CYGWIN__
+ #define DLL_PUBLIC __declspec(dllexport)
+#else
+ #if defined __GNUC__
+ #define DLL_PUBLIC __attribute__ ((visibility("default")))
+ #else
+ #pragma message ("Compiler does not support symbol visibility.")
+ #define DLL_PUBLIC
+ #endif
+#endif
+
+std::string DLL_PUBLIC getLibStr();
diff --git a/test cases/cmake/6 object library no dep/subprojects/cmObjLib/libB.hpp b/test cases/cmake/6 object library no dep/subprojects/cmObjLib/libB.hpp
index 71db6b7..52ccc16 100644
--- a/test cases/cmake/6 object library no dep/subprojects/cmObjLib/libB.hpp
+++ b/test cases/cmake/6 object library no dep/subprojects/cmObjLib/libB.hpp
@@ -2,4 +2,15 @@
#include <string>
-std::string getZlibVers();
+#if defined _WIN32 || defined __CYGWIN__
+ #define DLL_PUBLIC __declspec(dllexport)
+#else
+ #if defined __GNUC__
+ #define DLL_PUBLIC __attribute__ ((visibility("default")))
+ #else
+ #pragma message ("Compiler does not support symbol visibility.")
+ #define DLL_PUBLIC
+ #endif
+#endif
+
+std::string DLL_PUBLIC getZlibVers();