Skip to content

Commit

Permalink
Merge "Stop versioning LLVM libs (libc++, libc++abi, libclang-cpp)"
Browse files Browse the repository at this point in the history
  • Loading branch information
rprichard authored and Gerrit Code Review committed May 18, 2023
2 parents e0d6323 + 71c7c67 commit b18e0ba
Show file tree
Hide file tree
Showing 2 changed files with 59 additions and 102 deletions.
11 changes: 10 additions & 1 deletion base_builders.py
Original file line number Diff line number Diff line change
Expand Up @@ -757,6 +757,10 @@ def cmake_defines(self) -> Dict[str, str]:
defines['CLANG_DEFAULT_LINKER'] = 'lld'
defines['CLANG_DEFAULT_OBJCOPY'] = 'llvm-objcopy'

# Omit versions on LLVM's Linux and Darwin shared libraries. The versions for the runtimes
# (e.g. libc++) are also omitted, using OS-specific versions of the same CMake flag.
defines['CMAKE_PLATFORM_NO_VERSIONED_SONAME'] = 'ON'

if self._config.target_os.is_darwin:
defines['COMPILER_RT_ENABLE_IOS'] = 'OFF'
defines['COMPILER_RT_ENABLE_TVOS'] = 'OFF'
Expand All @@ -765,8 +769,12 @@ def cmake_defines(self) -> Dict[str, str]:
runtimes_cmake_args = []
runtimes_cmake_args.append(f'-DCMAKE_OSX_DEPLOYMENT_TARGET={constants.MAC_MIN_VERSION}')
runtimes_cmake_args.append('-DCMAKE_OSX_ARCHITECTURES=arm64|x86_64')
runtimes_cmake_args.append('-DCMAKE_PLATFORM_NO_VERSIONED_SONAME=ON')
defines['RUNTIMES_CMAKE_ARGS'] = ';'.join(sorted(runtimes_cmake_args))

# Add libc++abi to libc++.{a,dylib} for consistency with Linux.
defines['LIBCXX_ENABLE_STATIC_ABI_LIBRARY'] = 'ON'

if self._config.target_os.is_linux:
runtime_configs = [self._config]
if self.build_32bit_runtimes:
Expand Down Expand Up @@ -814,6 +822,7 @@ def cmake_defines(self) -> Dict[str, str]:
defines[f'RUNTIMES_{triple}_CMAKE_EXE_LINKER_FLAGS'] = ldflags_str
defines[f'RUNTIMES_{triple}_CMAKE_SHARED_LINKER_FLAGS'] = ldflags_str
defines[f'RUNTIMES_{triple}_CMAKE_MODULE_LINKER_FLAGS'] = ldflags_str
defines[f'RUNTIMES_{triple}_CMAKE_PLATFORM_NO_VERSIONED_SONAME'] = 'ON'

# clang generates call to builtin functions when building
# compiler-rt for musl. Allow use of the builtins library.
Expand All @@ -828,7 +837,7 @@ def cmake_defines(self) -> Dict[str, str]:

# Make libc++.so a symlink to libc++.so.x instead of a linker script that
# also adds -lc++abi. Statically link libc++abi to libc++ so it is not
# necessary to pass -lc++abi explicitly. This is needed only for Linux.
# necessary to pass -lc++abi explicitly.
defines[f'RUNTIMES_{triple}_LIBCXX_ENABLE_ABI_LINKER_SCRIPT'] = 'OFF'
defines[f'RUNTIMES_{triple}_LIBCXX_ENABLE_STATIC_ABI_LIBRARY'] = 'ON'

Expand Down
150 changes: 49 additions & 101 deletions do_build.py
Original file line number Diff line number Diff line change
Expand Up @@ -260,90 +260,6 @@ def install_wrappers(llvm_install_path: Path, llvm_next=False) -> None:
clangcl_path.symlink_to('clang.real')


# Normalize host libraries (libLLVM, libclang, libc++, libc++abi) so that there
# is just one library, whose SONAME entry matches the actual name.
def normalize_llvm_host_libs(install_dir: Path,
host: hosts.Host,
version: Version,
host_config: configs.Config) -> None:
if host.is_linux:
libs = {'libLLVM': 'libLLVM-{version}.so',
'libclang': 'libclang.so.{version}',
'libclang-cpp': 'libclang-cpp.so.{version}',
'libc++': 'libc++.so.{version}',
'libc++abi': 'libc++abi.so.{version}'
}
else:
libs = {'libc++': 'libc++.{version}.dylib',
'libc++abi': 'libc++abi.{version}.dylib'
}

def getVersions(libname: str) -> Tuple[str, str]:
if libname == 'libclang-cpp':
return version.major, version.major
if not libname.startswith('libc++'):
return version.long_version(), version.major
else:
return '1.0', '1'

no_llvm_libs = host_config.target_os.is_linux and host_config.is_32_bit
libdir = os.path.join(install_dir, 'lib')
for libname, libformat in libs.items():
if libformat.startswith('libc++'):
libprefix = os.path.join(libdir, host_config.llvm_triple)
if not os.path.exists(libprefix):
libprefix = libdir
elif no_llvm_libs:
continue
else:
libprefix = libdir

short_version, major = getVersions(libname)

if libname == 'libclang':
soname = list(Path(libprefix).glob('libclang.so.[0-9][0-9]'))
if len(soname) == 1:
soname_version = str(soname[0]).split('.')[-1]
else:
raise RuntimeError(str(len(soname)) + " versions of libclang.so found, 1 expected")
else:
soname_version = major

soname_lib = os.path.join(libprefix, libformat.format(version=soname_version))
if libname.startswith('libclang') and libname != 'libclang-cpp':
soname_lib = soname_lib[:-3]
real_lib = os.path.join(libprefix, libformat.format(version=short_version))

preserved_libnames = ('libLLVM', 'libclang-cpp')
if libname not in preserved_libnames and os.path.exists(real_lib):
# Rename the library to match its SONAME
if not os.path.isfile(real_lib):
raise RuntimeError(real_lib + ' must be a regular file')
if not os.path.islink(soname_lib):
raise RuntimeError(soname_lib + ' must be a symlink')

shutil.move(real_lib, soname_lib)

# Retain only soname_lib and delete other files for this library. We
# still need libc++.so or libc++.dylib symlinks for a subsequent stage1
# build using these prebuilts (where CMake tries to find C++ atomics
# support) to succeed. We also need a few checks to ensure libclang-cpp
# is not deleted when cleaning up libclang.so* and libc++abi is not
# deleted when cleaning up libc++.so*.
libcxx_name = 'libc++.so' if host.is_linux else 'libc++.dylib'
all_libs = [lib for lib in os.listdir(libprefix) if
lib != libcxx_name and
not lib.startswith('libclang-cpp') and # retain libclang-cpp
not lib.endswith('.a') and # skip static host libraries
(lib.startswith(libname + '.') or # so libc++abi is ignored
lib.startswith(libname + '-'))]

for lib in all_libs:
lib = os.path.join(libprefix, lib)
if lib != soname_lib:
os.remove(lib)


def install_license_files(install_dir: Path) -> None:
projects = (
'llvm',
Expand Down Expand Up @@ -431,6 +347,15 @@ def bolt_instrument(toolchain_builder: LLVMBuilder):
os.makedirs(clang_afdo_path, exist_ok=True)


def verify_symlink_exists(link_path: Path, target: Path):
if not link_path.exists():
raise RuntimeError(f'{link_path} does not exist')
if not link_path.is_symlink():
raise RuntimeError(f'{link_path} exists but is not a symlink')
if link_path.readlink() != target:
raise RuntimeError(f'{link_path} points to {link_path.readlink()}, expected {target}')


def verify_file_exists(lib_dir: Path, name: str):
if not (lib_dir / name).is_file():
raise RuntimeError(f'Did not find {name} in {lib_dir}')
Expand Down Expand Up @@ -584,6 +509,20 @@ def package_toolchain(toolchain_builder: LLVMBuilder,
'libc++.a',
'libc++abi.a',
}
if host.is_linux:
necessary_lib_files |= {
'libc++.so',
'libc++.so.1',
'libc++abi.so',
'libc++abi.so.1',
}
if host.is_darwin:
necessary_lib_files |= {
'libc++.dylib',
'libc++.1.dylib',
'libc++abi.dylib',
'libc++abi.1.dylib',
}

if host.is_windows and not win_sdk.is_enabled():
necessary_lib_files.add('libwinpthread-1' + shlib_ext)
Expand All @@ -596,27 +535,36 @@ def package_toolchain(toolchain_builder: LLVMBuilder,
if host.is_linux:
install_wrappers(install_dir, llvm_next)

if not host.is_windows:
normalize_llvm_host_libs(install_dir, host, version, host_config)
if host.is_linux:
# We also need to normalize the 32-bit libc++, libc++abi
normalize_llvm_host_libs(install_dir, host, version,
configs.host_32bit_config(host_config.is_musl))
# Add libc++[abi].so.1 and libc++[abi].1.dylib symlinks for backwards compatibility. These
# symlinks point to the unversioned libraries (as opposed to the typical situation where
# unversioned symlinks point to the versioned libraries).
if host.is_linux:
if host_config.is_musl:
triple32 = 'i686-unknown-linux-musl'
triple64 = 'x86_64-unknown-linux-musl'
else:
triple32 = 'i386-unknown-linux-gnu'
triple64 = 'x86_64-unknown-linux-gnu'
for tripleNN in (triple32, triple64):
(lib_dir / tripleNN / 'libc++.so.1').symlink_to('libc++.so')
(lib_dir / tripleNN / 'libc++abi.so.1').symlink_to('libc++abi.so')
(lib_dir / 'libc++.so.1').symlink_to(Path(triple64) / 'libc++.so.1')
(lib_dir / 'libc++abi.so.1').symlink_to(Path(triple64) / 'libc++abi.so.1')
elif host.is_darwin:
(lib_dir / 'libc++.1.dylib').symlink_to('libc++.dylib')
(lib_dir / 'libc++abi.1.dylib').symlink_to('libc++abi.dylib')

# Check necessary lib files exist.
for necessary_lib_file in necessary_lib_files:
if necessary_lib_file.startswith('libc++') and (host.is_linux or host.is_windows):
verify_file_exists(lib_dir / 'i686-w64-windows-gnu', necessary_lib_file)
verify_file_exists(lib_dir / 'x86_64-w64-windows-gnu', necessary_lib_file)
verify_file_exists(lib_dir, necessary_lib_file)
if necessary_lib_file.endswith('.a'):
verify_file_exists(lib_dir / 'i686-w64-windows-gnu', necessary_lib_file)
verify_file_exists(lib_dir / 'x86_64-w64-windows-gnu', necessary_lib_file)
if host.is_linux:
if host_config.is_musl:
verify_file_exists(lib_dir, necessary_lib_file)
verify_file_exists(lib_dir / 'i686-unknown-linux-musl', necessary_lib_file)
verify_file_exists(lib_dir / 'x86_64-unknown-linux-musl', necessary_lib_file)
else:
verify_file_exists(lib_dir, necessary_lib_file)
verify_file_exists(lib_dir / 'i386-unknown-linux-gnu', necessary_lib_file)
verify_file_exists(lib_dir / 'x86_64-unknown-linux-gnu', necessary_lib_file)
verify_symlink_exists(lib_dir / necessary_lib_file, Path(triple64) / necessary_lib_file)
verify_file_exists(lib_dir / triple32, necessary_lib_file)
verify_file_exists(lib_dir / triple64, necessary_lib_file)
else:
verify_file_exists(lib_dir, necessary_lib_file)

Expand Down Expand Up @@ -713,7 +661,7 @@ def package_toolchain(toolchain_builder: LLVMBuilder,
'clang++.real\n'
'clang-tidy\n'
'clang-tidy.real\n'
'../lib/libc++.so.1\n'
'../lib/libc++.so\n'
'lld\n'
'ld64.lld\n'
'ld.lld\n'
Expand Down

0 comments on commit b18e0ba

Please sign in to comment.