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.
pull/6379/head
Daniel Mensinger 5 years ago committed by Jussi Pakkanen
parent 7981308e6e
commit 77e0008a1f
  1. 11
      docs/markdown/snippets/cmake_improvements.md
  2. 64
      mesonbuild/cmake/interpreter.py
  3. 11
      test cases/cmake/15 object library advanced/main.cpp
  4. 13
      test cases/cmake/15 object library advanced/meson.build
  5. 18
      test cases/cmake/15 object library advanced/subprojects/cmObjLib/CMakeLists.txt
  6. 31
      test cases/cmake/15 object library advanced/subprojects/cmObjLib/genC.cpp
  7. 9
      test cases/cmake/15 object library advanced/subprojects/cmObjLib/libA.cpp
  8. 16
      test cases/cmake/15 object library advanced/subprojects/cmObjLib/libA.hpp
  9. 6
      test cases/cmake/15 object library advanced/subprojects/cmObjLib/libB.cpp
  10. 16
      test cases/cmake/15 object library advanced/subprojects/cmObjLib/libB.hpp
  11. 7
      test cases/cmake/5 object library/meson.build
  12. 13
      test cases/cmake/5 object library/subprojects/cmObjLib/libA.hpp
  13. 13
      test cases/cmake/5 object library/subprojects/cmObjLib/libB.hpp
  14. 5
      test cases/cmake/6 object library no dep/meson.build
  15. 13
      test cases/cmake/6 object library no dep/subprojects/cmObjLib/libA.hpp
  16. 13
      test cases/cmake/6 object library no dep/subprojects/cmObjLib/libB.hpp

@ -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

@ -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')

@ -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;
}

@ -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)

@ -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")

@ -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;
}

@ -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";
}

@ -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();

@ -0,0 +1,6 @@
#include "libB.hpp"
#include "libC.hpp"
std::string getZlibVers(void) {
return getGenStr();
}

@ -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();

@ -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')

@ -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();

@ -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();

@ -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')

@ -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();

@ -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();

Loading…
Cancel
Save