Repository navigation
Make add_openmp_flags_if_available() work for clang - #382
Conversation
|
Hi there @jamienoss 👋 - thanks for the pull request! I'm just a friendly 🤖 that checks for issues related to the changelog and making sure that this pull request is milestoned and labeled correctly. This is mainly intended for the maintainers, so if you are not a maintainer you can ignore this, and a maintainer will let you know if any action is required on your part 😃. Everything looks good from my point of view! 👍 If there are any issues with this message, please report them here. |
|
Let me know if there's a better/neater/preferred way of doing any of this. |
3d4b3be to
0eaacc9
Compare
|
This is required for astropy/astropy#7293 |
|
Thanks! I will review this next week. |
There was a problem hiding this comment.
Thanks for working on this! How portable is the solution related to LDFLAGS and CFLAGS on e.g Windows? Should we check that the platform is Linux/MacOS X and only fetch the extra flags from there?
Please update the test here:
to also make sure that OpenMP works on Mac.
Finally, please add a test for the generate_openmp_enabled_py functionality.
| } | ||
| """ | ||
|
|
||
| def _split_option_from_var(option, var, delim=' '): |
There was a problem hiding this comment.
This function needs a docstring for clarity (astropy-helpers code has typically been under-documented in the past, causing problems down the road, so we need to make sure all new code is easy to understand/read). Also please include some more comments below in the function to explain a bit more what is going on.
There was a problem hiding this comment.
I'm going to slightly refac this and rename it, sorry.
| log.info("Compiling Cython extension with OpenMP support") | ||
| extension.extra_compile_args.append(compile_flag) | ||
| extension.extra_link_args.append(link_flag) | ||
| log.info("Compiling Cython/C/C++/ extension with OpenMP support") |
| def _get_library_path(): | ||
| return _split_option_from_var('-L', 'LDFLAGS') | ||
|
|
||
| def add_openmp_flags_if_available(extension): |
There was a problem hiding this comment.
Add a note in the docstring that one can pass None to find out if OpenMP compilation would work without doing it for a specific extension.
There was a problem hiding this comment.
Will do. Maybe it's also a good idea to make it the default, extension=None, and also add a "wafer thin" wrapper with a more semantically correct name?
There was a problem hiding this comment.
Should probably suppress the exception when None also(?)
There was a problem hiding this comment.
I don't know if it's super important to have the semantically correct name, you can always just put a comment above the call to the function to explain what it does in that case?
| if packagename.lower() == 'astropy': | ||
| packagetitle = 'Astropy' | ||
| else: | ||
| packagetitle = 'Astropy-affiliated package ' + packagename |
There was a problem hiding this comment.
Not all packages using astropy-helpers are affiliated packages, so I would just set packagetitle = packagename
Good call, I'll make this conditional for now and we can address later(?). |
Sounds good! |
|
@astrofrog |
|
@astrofrog Regarding the new test |
|
@astrofrog hold..... |
c9fe414 to
8df39c8
Compare
|
@astrofrog and go... Sorry, I messed up the testing. I'm looking into what can be done about the OSX test but you're all good to review otherwise. Cheers |
729c7af to
56dd7b7
Compare
This was the first thing I tried and it didn't work. I tried it again, just now, as a sanity check but still nothing. I don't know why as this works fine on my machine, it was how I initially installed an OpenMP working clang, which is why I tried it first. Any ideas what might be going on? Also, there is an unstable failure of the Any ideas here? I tried adding in a sleep before the import (rationale=magic) but it did nothing. I wondered if it might be a caching issue but couldn't think how. I would gratefully appreciate some affirmative direction on this PR as I need to move on and spend my time elsewhere. |
|
I used |
I just noticed that CC=gcc occurs after the above... Trying again and moving the export to |
|
Trying |
|
@astrofrog I think this should be all done now. I got clang working via conda, I did the same for gcc on OSX to drop all of the scripts and reduce the number of OSX tests. I addressed the import stability issue by appending the current dir, |
astrofrog
left a comment
There was a problem hiding this comment.
Great that you got things working with the conda compilers! So just to check, do we still need the added complexity in terms of checking environment variables in the OpenMP helpers? Or do the conda compilers support -openmp properly?
Just to be clear, I'm in favor of the refactoring that makes it possible to know if a package was compiled with OpenMP, but I just want to make sure all the other changes are still needed.
|
|
||
| # Test OSX without OpenMP support | ||
| # Since the matrix OSX tests use the OS shipped version of clang, they double up | ||
| # as exploritory tests for when the shipped version has automatic OpenMP support. |
| # These tests will then fail and at such a time a new one should be added | ||
| # to explicitly remove OpenMP support. | ||
| - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then export TEST_OPENMP="False"; | ||
| else export TEST_OPENMP="True"; fi |
There was a problem hiding this comment.
Minor: I'd set TEST_OPENMP=True as a global higher up and just set it to False on MacOS X
There was a problem hiding this comment.
It's never good design to split conditionals like this. State logic should be as independent as it can be and contained within a single place. Splitting it out increases complexity and dependency, this is especially true here when considering stage overloading. Basically, it should only be set once and since this can't exist as a global (due to it being conditional on OS), it needs to exist within an actual eval stage.
If before_install is overloaded and TEST_OPENMP not set, the test will raise an exception here alerting the developer that they have forgotten to add this to their overloaded version.
Yes, they are needed to find and link with libomp.
They are. |
Well... the one thing I am slightly unsure of is how the path flags play out, especially I just spoke to @rendinam about this and I can try it out using |
The small openmp C test code used to determine if openmp works, fails to build with clang due to missing include and library paths for openmp. When run, it then fails due to rpath (runtime path) issues. This PR adds the necessary include, library, and runtime paths to the build. Signed-off-by: James Noss <[email protected]>
|
Note: Opened astropy/astropy#7409 |
|
@jamienoss - just to check, is this ready to merge from your side? |
Yes and no, sorry. I finally got the conda build working, using astropy/astropy#7293 as the test case. It built, tests passed but only when using gcc due to conda's LLVM (v4.0.1) bug, which is why I originally went with the manualy installs here. What I haven't managed to get around to, is taking that build and trying to install it on another machine or when anaconda has a diff path (i.e. not root - which it is on my (build) machine so may just end up working by coincidence). This last test would test any rpath concerns. Other than this though, it is good by me. |
|
@astrofrog Just tested it, it works. Only thing is that, again, this is with gcc and not clang and llvm, which this PR is semantically designed to address. The rpaths didn't create an issue. I checked them using What are your thoughts? |
| # These tests will then fail and at such a time a new one should be added | ||
| # to explicitly remove OpenMP support. | ||
| - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then export TEST_OPENMP="False"; | ||
| else export TEST_OPENMP="True"; fi |
There was a problem hiding this comment.
Remove the before_install section and define the env variables in the jobs above.
There was a problem hiding this comment.
I'll double check this, but if I remember correctly, it needs to be here so as to also work with the matrix permutations. There is no way to add this to them as they will be evaluated not at the correct stage, i.e. TRAVIS_OS_NAME is undefined. For the explicit tests it is, which is why I do so there also, but it isn't for the env:matrix: sec.
There was a problem hiding this comment.
Regardless of OS, TEST_OPENMP has to be set to something. If set as a global env and to true it will prevent the OSX tests from passing. If set to false it will prevent the linux tests from passing (unless we explicitly turn off the openmp support - which is altogether silly). One alternative is to make it such that all global matrixed OS tests pass with either state, which probably means getting OpenMP to work for all the OSX tests. The other alternative is to make TEST_OPENMP conditional and this therefore can't happen in the global env stage by definition. It has to go in the install or better still (due to overloading and its current use, not to mention semantically) in the before_install stage.
Let me know if there is an alternative, perhaps the entire idea needs to be re-thought, however, I honestly don't see the cause to not use the ``before_install_ stage.
| env: | ||
| - PYTHON_VERSION=3.5 | ||
| - CONDA_DEPENDENCIES="setuptools sphinx cython numpy pytest-cov clang llvm-openmp" | ||
| before_install: |
There was a problem hiding this comment.
I'm pretty sure there is no need for these before_install sections either, put those variable into the env section.
There was a problem hiding this comment.
So, I added these here following the Travis documentation. Also, because of the generic before_install, which then has to be overloaded per explicit job.
There was a problem hiding this comment.
| by the ``sphinx-astropy`` package in conjunction with the ``astropy-theme-sphinx``, | ||
| ``sphinx-automodapi``, and ``numpydoc`` packages. [#368] | ||
|
|
||
| - openmp_helpers.py: Make add_openmp_flags_if_available() work for clang. |
There was a problem hiding this comment.
This changelog is way too verbose, any chance to cut it back a bit? E.g. no need for filenames, but can use double backticks to highlight the name of the new functions.
| import sys | ||
| import glob | ||
| import tempfile | ||
| import subprocess |
There was a problem hiding this comment.
Please don't shuffle the imports around but just add the new ones, they were sorted by length on purpose.
There was a problem hiding this comment.
Woops, sorry, probably an auto-format.
There was a problem hiding this comment.
Do you mean import name length? If so, I should insert the two additions accordingly, right, rather than just append them to the list?
|
|
||
| def _get_flag_value_from_var(flag, var, delim=' '): | ||
| """ | ||
| Utility to extract `flag` value from `os.environ[`var`]` or, if not present, |
There was a problem hiding this comment.
use double backticks for parameters as a good practice, in public functions it would trip sphinx over otherwise (single backticks are for intersphinx links)
There was a problem hiding this comment.
Ok, cool, thanks for the intel, will do.
| Utility to extract `flag` value from `os.environ[`var`]` or, if not present, | ||
| from `distutils.sysconfig.get_config_var(`var`)`. | ||
| E.g. to get include path: _get_flag_value_from_var('-I', 'CFLAGS') | ||
| might return "/usr/local/include". |
There was a problem hiding this comment.
for which OS? Please keep comments it OS agnostic
There was a problem hiding this comment.
This is why I added the "E.g." and "might".
| try: | ||
| return {'compiler_flags':compile_flags, 'linker_flags':link_flags} | ||
|
|
||
| def test_openmp_support(openmp_flags=None, silent=False): |
There was a problem hiding this comment.
rename this function to check_openmp_support or something, it's very confusing to see a test function in the main codebase (and I'm not 100% certain that pytest likes it either).
There was a problem hiding this comment.
Good point, I hadn't thought of the pytest 'test_' namespace clash, cheers.
| Expecting `{'compiler_flags':<flags>, 'linker_flags':<flags>}`. | ||
| These are passed as `extra_postargs` to `compile()` and | ||
| `link_executable()` respectively. | ||
| silent : bool, optional |
There was a problem hiding this comment.
Why is this parameter needed? Surely the verbosity can already been tuned via the logger
There was a problem hiding this comment.
How? I couldn't find anything other than setting log.set_threshold(log.FATAL), which I am unsure of its semantic correctness.
bsipocz
left a comment
There was a problem hiding this comment.
There are still a few places that could benefit some cleanup.
| # Simple input validation | ||
| if not var or not flag: | ||
| return None | ||
| l = len(flag) |
There was a problem hiding this comment.
if possible avoid single letter variables
|
|
||
| openmp_flags = get_openmp_flags() | ||
| using_openmp = test_openmp_support(openmp_flags=openmp_flags, silent=False) | ||
| using_openmp = check_openmp_support(openmp_flags=openmp_flags, silent=False) |
There was a problem hiding this comment.
I think silent=False needs to be removed to avoid the current test failures
Signed-off-by: James Noss <[email protected]>
|
I tested this out locally for astropy-healpix and it does indeed work nicely. It also looks like @bsipocz's comments have been addressed so I'll go ahead and merge this. Thanks @jamienoss! |
The small openmp C test code, used to determine if openmp works, fails to build with clang due to missing include and library paths for openmp. When run, it then fails due to rpath (runtime path) issues.
This PR adds the necessary include, library, and runtime paths to the build.
Signed-off-by: James Noss [email protected]