Skip to content

Improve "undefined Makefile variables" warning and fix it for GNU make 4.4.1#12843

Merged
shindere merged 4 commits intoocaml:trunkfrom
dra27:gnu-make-4-4-1
Dec 21, 2023
Merged

Improve "undefined Makefile variables" warning and fix it for GNU make 4.4.1#12843
shindere merged 4 commits intoocaml:trunkfrom
dra27:gnu-make-4-4-1

Conversation

@dra27
Copy link
Copy Markdown
Member

@dra27 dra27 commented Dec 19, 2023

CI would dutifully report "Undefined Makefile variables detected" but leave the actual list of variables as an exercise for the reader. The list is now displayed too.

GNU make 4.4.1 triggers the warning on the way we presently invoke the stdlib Makefile. While that's likely to change soon, it's a nuisance now when working on a system which has GNU make 4.4.1 (bleeding-edge Linux, macOS, or Windows).

Previously, the CI result warned that undefined variables were detected,
but left the user to scroll further up the log to find them. The list is
now repeated!
@dra27
Copy link
Copy Markdown
Member Author

dra27 commented Dec 19, 2023

cc @shindere

@dra27
Copy link
Copy Markdown
Member Author

dra27 commented Dec 19, 2023

The workaround for flexdll/Makefile is mirrored in ocaml/flexdll#128, but we'll need the fix here regardless.

@gasche
Copy link
Copy Markdown
Member

gasche commented Dec 19, 2023

CI would dutifully report "Undefined Makefile variables detected" but leave the actual list of variables as an exercise for the reader. The list is now displayed too.

You are good at User Experience! We should nerd-snipe you into improving the compiler error messages. Maybe we should add some non-Windows-portable code in the type printer or something.

@dra27
Copy link
Copy Markdown
Member Author

dra27 commented Dec 19, 2023

🙂 Alas, the poor UX from before is also my fault (#12022)

stdlib/Makefile Outdated
ifeq "$(USE_BOOT)" ""
CAMLC=$(OCAMLRUN) $(COMPILER)
else
CAMLC=$(BOOT_OCAMLC) $(CAMLC_FLAGS)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This change looks a bit suspicious to me because the name CAMLC_FLAGS suggests that all CAMLC flags are going to go there, but most flag-passing does not use this variable at all.

It is also not clear which variable involved was previously undefined. Can you explain?

(In the previous version I am not sure how overriding CAMLC from the caller works if it is defined unconditionally here on line previously-27. Argh, I shouldn't look at Makefiles...)

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

If you compile this Dockerfile:

FROM ocaml/opam:alpine-3.19-opam
RUN git clone https://github.com/ocaml/ocaml.git && git -C ocaml checkout 3908dd368eaf391f671db33804a452ad1868fb3a
WORKDIR ocaml
RUN ./configure && make -j coldstart

The first line of coldstart results in this command:

make -C stdlib OCAMLRUN='$(ROOTDIR)/boot/ocamlrun' CAMLC='$(BOOT_OCAMLC) -use-prims ../runtime/primitives' all

Note that $(ROOTDIR) and $(BOOT_OCAMLC) are being passed verbatim - i.e. they get expanded in stdlib/Makefile, not in the root Makefile. If you then add --warn-undefined-variables to that call and run it manually, you get:

$ make --warn-undefined-variables -C stdlib OCAMLRUN='$(ROOTDIR)/boot/ocamlrun' CAMLC='$(BOOT_OCAMLC) -use-prims ../runtime/primitives' all
make: Entering directory '/home/opam/ocaml/stdlib'
../Makefile.common:119: warning: undefined variable 'BOOT_OCAMLC'
make: Nothing to be done for 'all'.
make: Leaving directory '/home/opam/ocaml/stdlib'

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

The trick I'm doing here is instead of passing the entire CAMLC macro and overriding the runtime (which is what the OCAMLRUN and CAMLC definitions before did), I'm passing enough information to stdlib/Makefile to tell it to do it. It needs to know that it's being called by coldstart and what the additional flags to pass to the compiler should be. Which arguably suggests my name CAMLC_FLAGS is bad (open to suggestions!).

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

With apologies for being slow, I still don't understand why BOOT_OCAMLC is missing there. It is defined in Makefile.common, which stdlib/Makefile correctly includes. I went to look at line 119 of Makefile.common, it reads like

ifeq "$(TEST_BOOT_OCAMLC_OPT)" "0"

and TEST_BOOT_OCAMLC_OPT does not seem to contain any call to CAMLC. Assuming there is an off-by-one disagreement in line numbers between make and editors, the line below is

  BOOT_OCAMLC = $(ROOTDIR)/boot/ocamlc.opt

which also does not look like it could result in such a warning.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Sorry, I've been unclear - I was trying to explain how the fix works, not what's presently wrong. I'm reasonably sure what's presently going wrong is a bug in GNU make 4.4.1, at the very least in the reporting of the location. Having diagnosed it as being that specific call, I elected to workaround it, as it blocks being able to run the undefined variable test in CI. I determined that if you removed the $(BOOT_OCAMLC) in make --warn-undefined-variables -C stdlib OCAMLRUN='$(ROOTDIR)/boot/ocamlrun' CAMLC='$(BOOT_OCAMLC) -use-prims ../runtime/primitives' all then the warning didn't trigger, so I therefore constructed a workaround which allowed me not to specify $(BOOT_OCAMLC) in that command-line.

I just tested, and the master branch of GNU make is still doing the same thing. Given that master is also reporting a fresh warning to do with our V_ macros it may be time for a bug report, but I was trying to focus on our Windows CI, and this is blocking it 🙂

@shindere
Copy link
Copy Markdown
Contributor

shindere commented Dec 20, 2023 via email

GNU make 4.4.1 is triggering an undefined variable warning on the use of
'$(BOOT_OCAMLC)' in a definition in the command line. It's not clear if
this is intentional or not, but as the whole mechanism will ultimately
disappear when stdlib/Makefile is merged into the root Makefile, this
provides a viable workaround for the present setup.
When called with MSVC_DETECT=0, flexdll/Makefile does not define
MSVCC_ROOT which triggers an undefined Makefile variable warning from
make. Fixed upstream, but it needs to be shimmed when called from OCaml,
regardless.
@dra27
Copy link
Copy Markdown
Member Author

dra27 commented Dec 21, 2023

Spaces added. I agree, USE_BOOT was a really bad name, especially as it implies the word bootstrap, which this is nothing to do with it! I changed that to USE_BOOT_OCAMLC, changed CAMLC_FLAGS to BOOT_OCAMLC_CFLAGS and added a very long comment (hopefully) explaining the three ways stdlib/Makefile gets invoked.

There is no need to pass -use-prims to the stdlib Makefile as it doesn't
link any executables, and primitive checking only ever occurs at
link-time.
@dra27
Copy link
Copy Markdown
Member Author

dra27 commented Dec 21, 2023

Following an offline discussion with @shindere, I realise that the -use-prims flag passed to stdlib/Makefile in coldstart is unnecessary. This was added in #324, when the concept of being able to add a primitive to the runtime and use it in the Standard Library without a bootstrap was added.

It looks like it was added on a "replace all" basis to any Makefile which used the boot compiler. However, the primitive checking only ever occurs when linking, so it cannot ever be necessary for stdlib/Makefile, which only builds libraries, to have to worry about the primitives list.

Removing it both removes the BOOT_CAMLC_FLAGS variable proposed here, but also the slightly awkward "filter-out" of -use-prims needs in the dependency rules for the Standard Library, which was a little bit of a zit.

(in a fit of nostaglia, I checked out the 4.04 branch and confirmed that this change could have been applied at the time; a more systematic approach is to begin at Symtable.init, which is the only place where the primitives list is loaded, and trace that through the compiler - it's only Bytelink's linking functions which ever call it)

@shindere
Copy link
Copy Markdown
Contributor

shindere commented Dec 21, 2023 via email

@xavierleroy
Copy link
Copy Markdown
Contributor

However, the primitive checking only ever occurs when linking, so it cannot ever be necessary for stdlib/Makefile, which only builds libraries, to have to worry about the primitives list.

That's a subtle point, but it is very well taken! Thanks for digging into this.

@shindere shindere merged commit b274ea5 into ocaml:trunk Dec 21, 2023
dra27 pushed a commit to dra27/ocaml that referenced this pull request Jul 29, 2024
(cherry picked from commit b274ea5)
dra27 pushed a commit to dra27/ocaml that referenced this pull request Jul 29, 2024
(cherry picked from commit b274ea5)
dra27 pushed a commit to dra27/ocaml that referenced this pull request Jul 29, 2024
(cherry picked from commit b274ea5)
dra27 pushed a commit to dra27/ocaml that referenced this pull request Jul 29, 2024
(cherry picked from commit b274ea5)
dra27 pushed a commit to dra27/ocaml that referenced this pull request Jul 29, 2024
(cherry picked from commit b274ea5)
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.

4 participants