Better Makefile target parsing#8223
Conversation
| break | ||
| else: | ||
| tty.msg('No Makefile found in the build directory') | ||
| return |
There was a problem hiding this comment.
This block isn't actually needed anymore, it is only used in the final output message. Do you think it's still worth keeping?
There was a problem hiding this comment.
What about something like:
if not any(os.path.exists(makefile) for makefile in ['GNUmakefile', 'Makefile', 'makefile']):
tty.msg('No Makefile found in the build directory')
return?
There was a problem hiding this comment.
I personally find this more readable, and you suggestion will need to be split onto multiple lines anyway. Plus, this results in makefile being set to the Makefile name, whereas I don't know if your suggestion does.
There was a problem hiding this comment.
Fair enough. Readability is subjective.
or you can fallback to the old code with regex if |
|
Is the BSD Make executable actually called |
|
BSD make was the original, and its executable was called I'm not against listing GNU make as a requirement; especially since it's already part of Linux and macOS, the two target platforms we run on. |
|
Ping ping |
| # | ||
| # https://www.gnu.org/software/make/manual/html_node/Running.html | ||
| # | ||
| # NOTE: This only works for GNU Make, not NetBSD Make. |
There was a problem hiding this comment.
When you say:
The downside to this approach is that if someone runs spack install --test on a system that uses BSD Make, it will always think that a check target exists and run it even if it doesn't.
How does this compare to the previous behavior for BSD make in Spack?
What is the exit status if you run this for BSD make? Can you distinguish a failing BSD make from a GNU make that is failing because the target doesn't exist?
There was a problem hiding this comment.
Previously, we would grep the file for a ^check: target. It didn't matter if it was BSD or GNU Make, it would behave identically. The problem was that this regex isn't powerful enough to capture all possible check targets (see first paragraph of PR description).
The current behavior is that it is guaranteed to work perfectly for GNU Make but doesn't work at all for BSD Make.
With BSD Make, there is no separate exit status for "this target doesn't exist", it's the same exit status as "the target has not yet been built", so there is no way to distinguish by exit status alone. We could fall back on a regex if we need to support BSD Make, although I've never encountered a system that uses BSD Make.
|
Ping @alalazo |
alalazo
left a comment
There was a problem hiding this comment.
Concerning BSD Make: I wonder how many people have only that at their disposal. My guess is not a lot. In that case the impact of this PR wouldn't be that heavy in the sense that people impacted by a loss of functionality are those running:
$ spack install --run-tests ...
on those few systems.
| break | ||
| else: | ||
| tty.msg('No Makefile found in the build directory') | ||
| return |
There was a problem hiding this comment.
What about something like:
if not any(os.path.exists(makefile) for makefile in ['GNUmakefile', 'Makefile', 'makefile']):
tty.msg('No Makefile found in the build directory')
return?
|
There are alternatives to this PR, or things we could try in the case of BSD Make: I would have to see how many of those flags work across all versions of Make though. |
|
Ping |
On account of that I'm inclined to merge this. If I understand correctly this would mean that for GNU makefiles without a check target you'd end up doing the I'll get a chance to read through this again by the end of Tuesday 7/17. |
|
@scheibelp Here's a summary of the previous and current behavior: BeforeIdentical behavior for GNU Make and BSD Make. No false positives, many false negatives. Ignored presence of check test:
test check:
check :
foo check bar:AfterDifferent behavior for GNU Make and BSD Make. No false positives or false negatives for GNU Make, 100% false positive rate for BSD Make. I'm not sure how common non-GNU Make is. If you are concerned, we could add logic to fall back on regexes if non-GNU Make is detected. If anyone can think of a better regex than |
Got it, I was incorrect in #8223 (comment): when you say "fall back to regex", we'd need to know that we are dealing with BSD make in the first place.
In other words I wanted to make sure we can figure out whether we are using BSD or GNU make if we need to (later, not in this PR), and based on the above I think we can. If you agree then I'm fine merging this as is (I'll get a chance to do that tomorrow). |
|
Yes, checking the output of |
|
Thanks! |
#8223 replaced regex-based makefile target parsing with an invocation of "make -q". #8818 discovered that "make -q" can result in an error for some packages. Also, the "make -q" strategy relied on interpreting the error code, which only worked for GNU Make and not BSD Make (which was deemed acceptable at the time). As an added bonus, this implementation ignores the exit code and instead parses STDERR for any indications that the target does not exist; this works for both GNU Make and BSD Make. #8223 also updated ninja target detection to use "ninja -t targets". This does not change that behavior but makes it more-explicit with "ninja -t targets all" This also adds tests for detection of "make" and "ninja" targets.
spack#8223 replaced regex-based makefile target parsing with an invocation of "make -q". spack#8818 discovered that "make -q" can result in an error for some packages. Also, the "make -q" strategy relied on interpreting the error code, which only worked for GNU Make and not BSD Make (which was deemed acceptable at the time). As an added bonus, this implementation ignores the exit code and instead parses STDERR for any indications that the target does not exist; this works for both GNU Make and BSD Make. spack#8223 also updated ninja target detection to use "ninja -t targets". This does not change that behavior but makes it more-explicit with "ninja -t targets all" This also adds tests for detection of "make" and "ninja" targets.
While working on the
cryptopppackage, I ran into aGNUmakefilethat didn't have a^check:or^test:target, it had a^test check:target. Apparently Makefiles can contain multiple targets that act as aliases. They can also have a space between the target and the colon. All of these things break our previous Makefile parsing logic.I started playing around with regexes, but things quickly became complicated. I discovered an easier way to use
maketo query whether or not a specific target exists.Pros
More reliable Makefile parsing.
Cons
This approach only works for GNU Make, it fails for BSD Make. I'm not sure how common BSD Make is (macOS and Linux both use GNU Make). The downside to this approach is that if someone runs
spack install --teston a system that uses BSD Make, it will always think that achecktarget exists and run it even if it doesn't. The solution would either be to install GNU Make or to re-runspack installwithout--testif the test crashes.P.S. I found a similar feature for Ninja and implemented that as well.