Skip to content

Compile GMP and MPFR from source#1985

Merged
alecjacobson merged 6 commits intomainfrom
alecjacobson/rosetta
Mar 14, 2022
Merged

Compile GMP and MPFR from source#1985
alecjacobson merged 6 commits intomainfrom
alecjacobson/rosetta

Conversation

@alecjacobson
Copy link
Copy Markdown
Contributor

@alecjacobson alecjacobson commented Feb 24, 2022

Previously we relied on gmp and mpfr to be installed system wide. This (work in progress) PR uses cmake's ExternalProject_add to download and compile gmp and mpfr from source in the build.

gmp and mpfr use configure/make style build which seems well supported by ExternalProject_add. I've also borrowed some configure flags from homebrew's build of these libraries (otherwise they were not easy to compile following the gmp/mpfr documentation).

This affects (and appears to work correctly) on Mac and Linux. A nice feature of compiling these from source, is that it's more straightforward to compile for a different architecture (e.g., compiling for x86_64 and using Rosetta on an M1 Mac).

I don't recall why we weren't compiling gmp/mpfr from source. Is the a reason besides this ExternalProject_add not existing/being written?

Windows continues to use precompiled binaries downloaded from CGAL.

I really banged my head on getting the ExternalProject_add paths working correctly. I think @jdumas had put some thought into the organization of subdirectories in the build/ folder. Would love to know if this can be similarly cleaned up. Currently it makes these build/gmp-install, build/gmp-build, etc. folders...

Copy link
Copy Markdown
Collaborator

@jdumas jdumas left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The main reason I've avoided using ExternalProject_Add for gmp/mpfr on Linux/macOS is that ExternalProject_Add is really more suited for a two-stage CMake build. The typical approach to using ExternalProject_Add with CMake is as follows:

  1. Run a "meta" CMakeLists.txt that uses ExternalProject_Add to download, compile & install all third-party libs into a desired target folder.
  2. Run your own project's CMakeLists.txt and use find_package() to import the target you have just installed in the previous step.

This requires the user to run cmake && make two times for the project, and I really don't like this approach.

The problem with trying to use ExternalProject_Add like you're doing right now, is that it's not easy to express the dependencies between building the external lib, and building the other targets that depend on it. Let's imagine for a moment that building libigl is super fast, and you attempt to link your libig_copyleft_cgal.a against gmp.a, but your external project hasn't finished building gmp. Then you're gonna get an error. Given how fast it is to build gmp/mpfr, and how slow it is to build libigl, this is not gonna happen very often. But looks like you've had similar issues with the include headers.

I'm pretty sure it's possible to express the dependency between gmp::gmp and the external project, to make sure the external lib is downloaded & compiled before anyone tries to use it. But this will require some additional logic from what I can tell.

ExternalProject_Get_Property(gmp SOURCE_DIR)
set(gmp_LIBRARIES ${gmp_LIBRARY})
add_library(gmp::gmp INTERFACE IMPORTED GLOBAL)
file(MAKE_DIRECTORY ${gmp_INCLUDE_DIR}) # avoid race condition
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can still get race condition if you try to compile a file that includes <gmp.h> before the external project is downloaded.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not for a race condition at make time, but rather at cmake time. The following line:

target_include_directories(gmp::gmp INTERFACE ${gmp_INCLUDE_DIR})

will fail if that include directory doesn't exist (it doesn't matter if the actual headers are there yet. just need the path to exist).

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(this comment # avoid race condition is from a copy-paste. I don't really agree that it's a race condition. It has nothing to do with threads AFAICT. It's rather just that the directory doesn't exist yet)

@alecjacobson
Copy link
Copy Markdown
Contributor Author

I haven't run into that issue yet. Are you sure the dependencies aren't handled correctly?

Consider this mini project https://github.com/alecjacobson/gmp-mpfr-externalproject_add

If I run make -j8, then it still waits for gmp and mpfr to build before compiling and linking the executable.

@jdumas
Copy link
Copy Markdown
Collaborator

jdumas commented Feb 24, 2022

Ah you're right, it looks like the line add_dependencies(gmp::gmp gmp) does the trick of expressing the dependency properly at CMake level. I think this should work then! Guess the reason we didn't have this before is because nobody did it :)

@alecjacobson
Copy link
Copy Markdown
Contributor Author

Thanks. Do you have any suggestions about the build sub-directories organization? I got super confused trying to use PREFIX / DOWNLOAD_DIR / SOURCE_DIR etc. but it seems possible to clean this up or move everything into the _deps folder that gets created.

@jdumas
Copy link
Copy Markdown
Collaborator

jdumas commented Feb 25, 2022

If you include(FetchContent), you can use ${FETCHCONTENT_BASE_DIR} to retrieve the location of the _deps/ folder.

@alecjacobson
Copy link
Copy Markdown
Contributor Author

fwiw, this worked successfully for my immediate use case: compile libigl-based mex functions and running them on an M1 mac running Matlab using Rosetta.

@jdumas
Copy link
Copy Markdown
Collaborator

jdumas commented Feb 28, 2022

Make sure you remove gmp/mpfr's install for Linux/macOS on GitHub Actions before merging this:


brew install gmp mpfr ccache

@alecjacobson alecjacobson merged commit f9547b8 into main Mar 14, 2022
@alecjacobson alecjacobson deleted the alecjacobson/rosetta branch March 14, 2022 17:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants