From 20f761a01e2c02361d577921221b97dfb881b262 Mon Sep 17 00:00:00 2001 From: Adam Novak Date: Tue, 17 Dec 2024 18:10:15 -0500 Subject: [PATCH 1/5] Take over managing pybind11 and use binder that can use the right llvm --- CMakeLists.txt | 13 +++++++++---- make_and_run_binder.py | 35 ++++++++++++++++++++++++++++------- 2 files changed, 37 insertions(+), 11 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index cefd3d6f..302ac164 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -189,10 +189,13 @@ target_include_directories(sparsepp INTERFACE "${bdsg_DIR}/deps/sparsepp/") add_subdirectory("${bdsg_DIR}/deps/mio") if (BUILD_PYTHON_BINDINGS) + # Binder (because some generated bindings depend on headers packaged with Binder) + # See also: Binder commit defined in make_and_run_binder.py which actually generates bindings. + set(BINDER_COMMIT a1f81c86b75075aea2a7aab2c02f45f95fd5fe84) ExternalProject_Add(binder - GIT_REPOSITORY "https://github.com/RosettaCommons/binder.git" - GIT_TAG "ee2ecff151d125c3add072a7765aebad6f42a70d" + GIT_REPOSITORY "https://github.com/adamnovak/binder.git" + GIT_TAG "${BINDER_COMMIT}" # we don't actually build or install Binder via its CMake because we just need its headers #CMAKE_ARGS -DCMAKE_INSTALL_PREFIX:PATH=${INSTALL_DIR} CONFIGURE_COMMAND "" @@ -202,6 +205,8 @@ if (BUILD_PYTHON_BINDINGS) set(binder_INCLUDE "${INSTALL_DIR}/${CMAKE_INSTALL_INCLUDEDIR}") # pybind11 + # See also: pybind11 commit defined in make_and_run_binder.py. + set(PYBIND11_COMMIT 5b0a6fc2017fcc176545afe3e09c9f9885283242) if (CMAKE_MAJOR_VERSION EQUAL "3" AND CMAKE_MINOR_VERSION EQUAL "10") # We need pybind11 installed in ./pybind11 *before* CMake can finish processing this file. # On CMake 3.11+ we can do that with FetchContent @@ -209,7 +214,7 @@ if (BUILD_PYTHON_BINDINGS) if (NOT EXISTS "${PROJECT_SOURCE_DIR}/pybind11") message(WARNING "Running on CMake without FetchContent_Declare; attempting to download pybind11 manually") execute_process(COMMAND git clone https://github.com/RosettaCommons/pybind11.git "${PROJECT_SOURCE_DIR}/pybind11") - execute_process(COMMAND git checkout 5b0a6fc2017fcc176545afe3e09c9f9885283242 + execute_process(COMMAND git checkout "${PYBIND11_COMMIT}" WORKING_DIRECTORY "${PROJECT_SOURCE_DIR}/pybind11") endif() @@ -225,7 +230,7 @@ if (BUILD_PYTHON_BINDINGS) FetchContent_Declare( pybind11 GIT_REPOSITORY https://github.com/RosettaCommons/pybind11.git - GIT_TAG 5b0a6fc2017fcc176545afe3e09c9f9885283242 + GIT_TAG "${PYBIND11_COMMIT}" ) FetchContent_GetProperties(pybind11) if (NOT pybind11_POPULATED) diff --git a/make_and_run_binder.py b/make_and_run_binder.py index 15496fc4..122cb8cb 100755 --- a/make_and_run_binder.py +++ b/make_and_run_binder.py @@ -23,19 +23,31 @@ python_module_name = 'bdsg' # We have one global notion of what an include looks like -INCLUDE_REGEX = re.compile('^\s*#include\s+(["<])(.*)([">])') +INCLUDE_REGEX = re.compile(r'^\s*#include\s+(["<])(.*)([">])') # We have one master list of source code extensions SOURCE_EXTENSIONS = ['hpp', 'cpp', 'h', 'cc', 'c'] def clone_repos(): - ''' download the most recent copy of binder from git ''' + ''' download the most correct binder and pybind11 from git ''' if not glob.glob("binder"): print("Binder not found, cloning repo...") - subprocess.check_call(['git', 'clone', 'https://github.com/RosettaCommons/binder.git', 'binder']) + subprocess.check_call(['git', 'clone', 'https://github.com/adamnovak/binder.git', 'binder']) parent = os.getcwd() os.chdir('binder') - subprocess.check_call(['git', 'checkout', 'ee2ecff151d125c3add072a7765aebad6f42a70d']) + # See also: Binder commit defined in CMakeLists.txt for header files. + subprocess.check_call(['git', 'checkout', 'a1f81c86b75075aea2a7aab2c02f45f95fd5fe84']) os.chdir(parent) + if not glob.glob("binder/build/pybind11"): + print("pybind11 not found, cloning repo...") + parent = os.getcwd() + os.chdir('binder') + os.makedirs('build', exist_ok=True) + subprocess.check_call(['git', 'clone', 'https://github.com/RosettaCommons/pybind11.git', 'build/pybind11']) + os.chdir('build/pybind11') + # See also: pybind11 commit defined in CMakeLists.txt + subprocess.check_call(['git', 'checkout', '5b0a6fc2017fcc176545afe3e09c9f9885283242']) + os.chdir(parent) + def build_binder(): ''' @@ -46,10 +58,19 @@ def build_binder(): ''' if not glob.glob("./build/*/*/bin/*"): print("Binder not compiled, using packaged build.py...") - # Make Binder use out pybind11 version - subprocess.check_call(['sed', '-i', "s/^_pybind11_version_ = .*/_pybind11_version_ = '5b0a6fc2017fcc176545afe3e09c9f9885283242'/g", 'build.py']) # TODO: Use CPU counting that accounts for container quotas? - subprocess.check_call([sys.executable, 'build.py', '--jobs', str(multiprocessing.cpu_count())]) + subprocess.check_call( + [ + sys.executable, + 'build.py', + '--compiler', + 'clang' if platform.system() == 'Darwin' else 'gcc', + '--jobs', + str(multiprocessing.cpu_count()), + '--pybind11', + os.path.join(os.getcwd(), 'build/pybind11') + ] + ) return "binder/" + glob.glob('./build/*/*/bin/')[0] + "binder" def all_sources_and_headers(include_deps=False): From 111ad8becf0c8fe704c81b1a479f46eb24b6e726 Mon Sep 17 00:00:00 2001 From: Adam Novak Date: Tue, 17 Dec 2024 19:44:40 -0500 Subject: [PATCH 2/5] Use Binder that doesn't try to build the llvm benchmarks --- CMakeLists.txt | 2 +- make_and_run_binder.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 302ac164..4741a793 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -192,7 +192,7 @@ if (BUILD_PYTHON_BINDINGS) # Binder (because some generated bindings depend on headers packaged with Binder) # See also: Binder commit defined in make_and_run_binder.py which actually generates bindings. - set(BINDER_COMMIT a1f81c86b75075aea2a7aab2c02f45f95fd5fe84) + set(BINDER_COMMIT d5ef611f80cc91848db1fbd09d26b97013aa4db5) ExternalProject_Add(binder GIT_REPOSITORY "https://github.com/adamnovak/binder.git" GIT_TAG "${BINDER_COMMIT}" diff --git a/make_and_run_binder.py b/make_and_run_binder.py index 122cb8cb..3155ce2e 100755 --- a/make_and_run_binder.py +++ b/make_and_run_binder.py @@ -35,7 +35,7 @@ def clone_repos(): parent = os.getcwd() os.chdir('binder') # See also: Binder commit defined in CMakeLists.txt for header files. - subprocess.check_call(['git', 'checkout', 'a1f81c86b75075aea2a7aab2c02f45f95fd5fe84']) + subprocess.check_call(['git', 'checkout', 'a270d8f8e8b2e0a9638bcf80b16e466e70ac8dc0']) os.chdir(parent) if not glob.glob("binder/build/pybind11"): print("pybind11 not found, cloning repo...") From 4b179367d53f7648c2c2a123f336f70df7573ea7 Mon Sep 17 00:00:00 2001 From: Adam Novak Date: Tue, 17 Dec 2024 21:54:14 -0500 Subject: [PATCH 3/5] Show Binder where omp.h is, not that it can parse it --- make_and_run_binder.py | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/make_and_run_binder.py b/make_and_run_binder.py index 3155ce2e..30a65e44 100755 --- a/make_and_run_binder.py +++ b/make_and_run_binder.py @@ -35,7 +35,7 @@ def clone_repos(): parent = os.getcwd() os.chdir('binder') # See also: Binder commit defined in CMakeLists.txt for header files. - subprocess.check_call(['git', 'checkout', 'a270d8f8e8b2e0a9638bcf80b16e466e70ac8dc0']) + subprocess.check_call(['git', 'checkout', 'd5ef611f80cc91848db1fbd09d26b97013aa4db5']) os.chdir(parent) if not glob.glob("binder/build/pybind11"): print("pybind11 not found, cloning repo...") @@ -249,6 +249,16 @@ def make_bindings_code(all_includes_fn, binder_executable): # Also make sure to look for libomp from macports or homebrew, like CMakeLists.txt does command.append('-I/opt/local/include/libomp') command.append('-I/usr/local/include') + else: + # With current GCC, Clang can't find the multiarch-specific *and* + # GCC-version-specific include path where the OpenMP headers live. + # So help it out. + # TODO: We're assuming we're using GCC. + compiler_version = int(subprocess.check_output(["gcc", "-dumpversion"]).decode('utf-8').split('.')[0]) + compiler_triple = subprocess.check_output(["gcc", "-dumpmachine"]).decode('utf-8').strip() + command.append('-I' + f"/usr/lib/gcc/{compiler_triple}/{compiler_version}/include") + # TODO: It also can't *parse* the GCC13 omp.h once it finds it, due to https://github.com/llvm/llvm-project/issues/51607 + # But it seems to generate bindings anyway? # Find Jansson jansson_flags = subprocess.check_output(['pkg-config', '--cflags', 'jansson']).decode('utf-8').strip().split(' ') From 77d8c3fcd0ffe95c3d5d5ab34d5d76c9a85210b8 Mon Sep 17 00:00:00 2001 From: Adam Novak Date: Wed, 18 Dec 2024 11:52:21 -0500 Subject: [PATCH 4/5] Use macros to hide GCC's hard attributes from Binder --- bdsg/include/bdsg/internal/binder_hook_bind.hpp | 8 ++++++++ make_and_run_binder.py | 4 ++-- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/bdsg/include/bdsg/internal/binder_hook_bind.hpp b/bdsg/include/bdsg/internal/binder_hook_bind.hpp index d456779f..41029de0 100644 --- a/bdsg/include/bdsg/internal/binder_hook_bind.hpp +++ b/bdsg/include/bdsg/internal/binder_hook_bind.hpp @@ -4,6 +4,14 @@ // Components needed at binding generation time to make pybind11/Binder work for the library. // Forced to be used as a source for things to bind, even though nothing includes it. +// We need to include the OpenMP header with compiler attribute support +// disabled, because Binder can't understand the malloc attribute used in GCC +// 13's omp.h. See . +// Do this before anything else can use omp.h. +#define __attribute__(...) +#include +#undef __attribute__ + #include #include diff --git a/make_and_run_binder.py b/make_and_run_binder.py index 30a65e44..10682ad3 100755 --- a/make_and_run_binder.py +++ b/make_and_run_binder.py @@ -257,8 +257,8 @@ def make_bindings_code(all_includes_fn, binder_executable): compiler_version = int(subprocess.check_output(["gcc", "-dumpversion"]).decode('utf-8').split('.')[0]) compiler_triple = subprocess.check_output(["gcc", "-dumpmachine"]).decode('utf-8').strip() command.append('-I' + f"/usr/lib/gcc/{compiler_triple}/{compiler_version}/include") - # TODO: It also can't *parse* the GCC13 omp.h once it finds it, due to https://github.com/llvm/llvm-project/issues/51607 - # But it seems to generate bindings anyway? + # We rely on macro hacks in binder_hook_bind.hpp to translate the file + # into something Binder can understand. # Find Jansson jansson_flags = subprocess.check_output(['pkg-config', '--cflags', 'jansson']).decode('utf-8').strip().split(' ') From 35de57eb51211ba98496bab3912199c3dddc80da Mon Sep 17 00:00:00 2001 From: Adam Novak Date: Wed, 18 Dec 2024 12:25:15 -0500 Subject: [PATCH 5/5] Use Binder that tracks LLVM version of CMake files --- CMakeLists.txt | 2 +- make_and_run_binder.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 4741a793..a80916e4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -192,7 +192,7 @@ if (BUILD_PYTHON_BINDINGS) # Binder (because some generated bindings depend on headers packaged with Binder) # See also: Binder commit defined in make_and_run_binder.py which actually generates bindings. - set(BINDER_COMMIT d5ef611f80cc91848db1fbd09d26b97013aa4db5) + set(BINDER_COMMIT b6cac94c78ade6c6ffcbda629ffa520561a31788) ExternalProject_Add(binder GIT_REPOSITORY "https://github.com/adamnovak/binder.git" GIT_TAG "${BINDER_COMMIT}" diff --git a/make_and_run_binder.py b/make_and_run_binder.py index 10682ad3..7e7a0547 100755 --- a/make_and_run_binder.py +++ b/make_and_run_binder.py @@ -35,7 +35,7 @@ def clone_repos(): parent = os.getcwd() os.chdir('binder') # See also: Binder commit defined in CMakeLists.txt for header files. - subprocess.check_call(['git', 'checkout', 'd5ef611f80cc91848db1fbd09d26b97013aa4db5']) + subprocess.check_call(['git', 'checkout', 'b6cac94c78ade6c6ffcbda629ffa520561a31788']) os.chdir(parent) if not glob.glob("binder/build/pybind11"): print("pybind11 not found, cloning repo...")