# This file is licensed under the Apache License v2.0 with LLVM Exceptions. # See https://llvm.org/LICENSE.txt for license information. # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception """Helper macros to configure the LLVM overlay project.""" load("@bazel_tools//tools/build_defs/repo:utils.bzl", "maybe") load(":zlib.bzl", "llvm_zlib_disable", "llvm_zlib_system") load(":terminfo.bzl", "llvm_terminfo_disable", "llvm_terminfo_system") # Directory of overlay files relative to WORKSPACE DEFAULT_OVERLAY_PATH = "llvm-project-overlay" DEFAULT_TARGETS = [ "AArch64", "AMDGPU", "ARM", "AVR", "BPF", "Hexagon", "Lanai", "LoongArch", "Mips", "MSP430", "NVPTX", "PowerPC", "RISCV", "Sparc", "SystemZ", "VE", "WebAssembly", "X86", "XCore", ] def _overlay_directories(repository_ctx): src_path = repository_ctx.path(Label("@llvm-raw//:WORKSPACE")).dirname bazel_path = src_path.get_child("utils").get_child("bazel") overlay_path = bazel_path.get_child("llvm-project-overlay") script_path = bazel_path.get_child("overlay_directories.py") python_bin = repository_ctx.which("python3") if not python_bin: # Windows typically just defines "python" as python3. The script itself # contains a check to ensure python3. python_bin = repository_ctx.which("python") if not python_bin: fail("Failed to find python3 binary") cmd = [ python_bin, script_path, "--src", src_path, "--overlay", overlay_path, "--target", ".", ] exec_result = repository_ctx.execute(cmd, timeout = 20) if exec_result.return_code != 0: fail(("Failed to execute overlay script: '{cmd}'\n" + "Exited with code {return_code}\n" + "stdout:\n{stdout}\n" + "stderr:\n{stderr}\n").format( cmd = " ".join([str(arg) for arg in cmd]), return_code = exec_result.return_code, stdout = exec_result.stdout, stderr = exec_result.stderr, )) def _extract_cmake_settings(repository_ctx, llvm_cmake): # The list to be written to vars.bzl # `CMAKE_CXX_STANDARD` may be used from WORKSPACE for the toolchain. c = { "CMAKE_CXX_STANDARD": None, "LLVM_VERSION_MAJOR": None, "LLVM_VERSION_MINOR": None, "LLVM_VERSION_PATCH": None, } # It would be easier to use external commands like sed(1) and python. # For portability, the parser should run on Starlark. llvm_cmake_path = repository_ctx.path(Label("//:" + llvm_cmake)) for line in repository_ctx.read(llvm_cmake_path).splitlines(): # Extract "set ( FOO bar ... " setfoo = line.partition("(") if setfoo[1] != "(": continue if setfoo[0].strip().lower() != "set": continue # `kv` is assumed as \s*KEY\s+VAL\s*\).* # Typical case is like # LLVM_REQUIRED_CXX_STANDARD 17) # Possible case -- It should be ignored. # CMAKE_CXX_STANDARD ${...} CACHE STRING "...") kv = setfoo[2].strip() i = kv.find(" ") if i < 0: continue k = kv[:i] # Prefer LLVM_REQUIRED_CXX_STANDARD instead of CMAKE_CXX_STANDARD if k == "LLVM_REQUIRED_CXX_STANDARD": k = "CMAKE_CXX_STANDARD" c[k] = None if k not in c: continue # Skip if `CMAKE_CXX_STANDARD` is set with # `LLVM_REQUIRED_CXX_STANDARD`. # Then `v` will not be desired form, like "${...} CACHE" if c[k] != None: continue # Pick up 1st word as the value. # Note: It assumes unquoted word. v = kv[i:].strip().partition(")")[0].partition(" ")[0] c[k] = v # Synthesize `LLVM_VERSION` for convenience. c["LLVM_VERSION"] = "{}.{}.{}".format( c["LLVM_VERSION_MAJOR"], c["LLVM_VERSION_MINOR"], c["LLVM_VERSION_PATCH"], ) return c def _write_dict_to_file(repository_ctx, filepath, header, vars): # (fci + individual vars) + (fcd + dict items) + (fct) fci = header fcd = "\nllvm_vars={\n" fct = "}\n" for k, v in vars.items(): fci += '{} = "{}"\n'.format(k, v) fcd += ' "{}": "{}",\n'.format(k, v) repository_ctx.file(filepath, content = fci + fcd + fct) def _llvm_configure_impl(repository_ctx): _overlay_directories(repository_ctx) llvm_cmake = "llvm/CMakeLists.txt" vars = _extract_cmake_settings( repository_ctx, llvm_cmake, ) _write_dict_to_file( repository_ctx, filepath = "vars.bzl", header = "# Generated from {}\n\n".format(llvm_cmake), vars = vars, ) # Create a starlark file with the requested LLVM targets. targets = repository_ctx.attr.targets repository_ctx.file( "llvm/targets.bzl", content = "llvm_targets = " + str(targets), executable = False, ) llvm_configure = repository_rule( implementation = _llvm_configure_impl, local = True, configure = True, attrs = { "targets": attr.string_list(default = DEFAULT_TARGETS), }, ) def llvm_disable_optional_support_deps(): maybe( llvm_zlib_disable, name = "llvm_zlib", ) maybe( llvm_terminfo_disable, name = "llvm_terminfo", ) def llvm_use_system_support_deps(): maybe( llvm_zlib_system, name = "llvm_zlib", ) maybe( llvm_terminfo_system, name = "llvm_terminfo", )