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.
This commit is contained in:
parent
7981308e6e
commit
77e0008a1f
|
@ -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…
Reference in New Issue