Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cease storing C dependencies in the repository #9332

Merged
merged 4 commits into from
May 1, 2020

Conversation

dra27
Copy link
Member

@dra27 dra27 commented Feb 26, 2020

This is a proof-of-concept (with a few minor decisions to be taken noted in the code) of a discussion on caml-devel. This PR eliminates the need for storing C dependencies in the repository.

** OCaml dependencies are entirely different kettle of 🐟 and should be left out of this discussion, please **

When building for the first time, the only requirement is that generated header files have been built (jumptbl.h, version.h and opnames.h). Detailed dependency information is only required when headers have been edited.

COMPUTE_DEPS in Makefile.build_config controls whether C dependency information should be generated on a per-file basis. This variable is controlled by a new --disable-dependency-generation in configure which is enabled for Git checkouts and disabled for tarballs (i.e. releases).

The Microsoft C compiler (cl) cannot generate dependencies in a consistent way which we can consume, so for a Git checkout configure searches for an additional C compiler in order to compute dependencies. This is obviously not required for a user-build.

As a result, the MSVC port can now safely run make alldepend, since only OCaml dependency information is committed to the repo after this change.

CI does not need to waste time testing the dependency information, because it only tests a single build. A single Travis job has been added which tests the build system code to generate the dependency information (and provides a single make -j run in CI, although Inria's CI also
tests parallel building continuously).

This branch is running through precheck

cc @shindere

.gitattributes Outdated Show resolved Hide resolved
Comment on lines -189 to -193
/runtime/interp.a.lst
/runtime/*.[sd]obj
/runtime/.gdb_history
/runtime/*.d.c
/runtime/*.pic.c
Copy link
Member Author

Choose a reason for hiding this comment

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

I think these are legacy from before the merge of the runtime directories, @shindere?

.travis.yml Outdated Show resolved Hide resolved
@shindere
Copy link
Contributor

shindere commented Feb 26, 2020 via email

@dra27
Copy link
Member Author

dra27 commented Feb 26, 2020

Thanks, @shindere. I'm confused by your early comments: I haven't touched Changes yet and the only bootstrap file I've touched is tools/ci/inria/bootstrap, or is the merge commit you're looking at mentioning some other stuff?

The clean-up in .gitignore relates to removing what looked like old either dependency information or possibly debugging objects - either way, I was intentionally touching other lines nearby and ensuring that the entries were correct for runtime/.

There is an immediate use for --disable-dependency-generation in the PR itself - all the CI alterations are to use it, as there's no point wasting 10 seconds or so per job generating the C dependencies. One new job in Travis simultaneously tests parallel build and also forces the dependency generation.

-include would fail without error if the files cannot be included, which is not what I want - if dependencies are supposed to be being computed, then you want that to be an error. This is the include (or the $(foreach which generates it) is guarded by a test on $(COMPUTE_DEPS).

The order-only dependency is a good catch (and should have been on my TODO list) - in a first version I called the directory .depend but changed it to ensure that it would never match up with a filename we'd used before).

No opinion on the extension; I'm happy to change it.

In runtime/, foo.c causes .dep/foo-byt.depend and .dep/foo-nat.depend to be generated; the reason for separating them is that the building of these files takes a non-trivial amount of time and if you've configured without the native code compiler then it speeds things up. Changing that wouldn't be particularly simpler - the recipes would simply be concatenated (they're just a refactoring of what was already there).

@shindere
Copy link
Contributor

Sorry for the long silence on this. I just realised after talking to
@dra27 over the phone that I was waiting for feedback about a comment that
had actually never been sent :(

In this comment, I was suggesting to rewrite the rules generating the
dependencies to take advantage of a few gcc options that may avoid
the use of sed scripts.

Among these options are:

  • '-MF FILE' which lets you specify in which file to write the
    dependencies. As explained in gcc's documentation, this lets you separate
    the dependencies from other preprocessor output.Although these are not
    supposed to happen, they actually could and it wouln't hurt to use
    this option, I think.

  • '-MG', which assumes missing header files will be generated and adds them
    to the list of prerequisites may be of interest, but I am not completely
    sure.

  • '-MT target' or '-MQ target' let you specify the name of the target,
    the difference being that '-MQ' quotes special charactgers. Of all the
    optiosn, this is the one that looks most interesting to me because
    it would avoid the use of sed scripts. One could thus get rid of the
    for loops and use pattern rules to generate the dependency files.

@dra27 dra27 force-pushed the ocaml-dyndepend branch 2 times, most recently from d98fc9a to e3323a8 Compare April 15, 2020 17:12
@dra27
Copy link
Member Author

dra27 commented Apr 15, 2020

No problem, @shindere! Here's some work on this - -MT makes it much neater, indeed - sed is gone. I haven't used -MF because runtime/Makefile embeds multiple rules in the same file (so > followed by >> is sort of neater). Is it definitely worth it?

There's still a question of when to remove .dep in ocamltest/Makefile L287, otherlibs/Makefile.otherlibs.common L132, otherlibs/systhreads/Makefile L114 and runtime/Makefile L211 - do you think these should be deleted on make clean (which is what it currently does) or make distclean?

@shindere
Copy link
Contributor

shindere commented Apr 16, 2020 via email

@dra27 dra27 force-pushed the ocaml-dyndepend branch 2 times, most recently from c97ea15 to a681803 Compare April 16, 2020 10:20
@dra27
Copy link
Member Author

dra27 commented Apr 16, 2020

Yes, I had considered splitting up the .d files in runtime/, but it still wasn't clear to me that it would be much neater... but then I found in the manual that you can include wildcards. This allows one to generate .dep/Makefile (which is a single line and is acting much more like a make cookie than an actual Makefile, which is why it's written with @echo cf. @touch normally) and have that depend on the .d files which are required. That gets rid of all the foreach stuff and does indeed make the rules look less cryptic. runtime/Makefile is still complicated (and, to a lesser extent, otherlibs/systhreads/Makefile) because of building multiple object files from the same C source file, but generating the dependency information has not increased any of the complexity.

GNU make's pattern substitutions don't let you have two wildcards, which is occasionally limiting (so you can't, for example, trim off _% from strings). For this reason, I've altered the C object names to be .b.o instead of _b.o which I don't think makes any material difference anywhere, but does allow $(basename to operate on the filenames. It's also handy because we do have C files where the basename includes _ but of course no C files which include ..

I think arguably the C dependencies should probably be in distclean but, given that you could be working on ocamldep it's clear that you'd expect OCaml dependencies to be blown away in clean, so I think I'll leave the C dependencies being erased in clean. It's largely moot anyway, as C dependencies regenerate very quickly.

Assuming this passes CI and precheck, this is ready for final review.

@shindere
Copy link
Contributor

shindere commented Apr 16, 2020 via email

@dra27
Copy link
Member Author

dra27 commented Apr 16, 2020

I have already rewritten the history, yes - the bulk of the work is in the first commit; the two testsuite changes are separated in order to make the blame explicit (the change is related to testing alterations as part of the first commit, rather than being specifically because of dynamic dependency generation)

@dra27
Copy link
Member Author

dra27 commented Apr 16, 2020

Just a note that the precheck run passed, as has Travis (AppVeyor is just finishing up, but it looks to be green too)

@shindere
Copy link
Contributor

shindere commented Apr 17, 2020 via email

dra27 and others added 4 commits April 17, 2020 13:53
This moves the configure-generated parts of Makefile.common to a
separate (generated) Makefile, allowing Makefile.common to be a normal
Makefile.

OCaml's build system Makefile's now include Makefile.build_config (which
itself includes Makefile.config) but Makefile.config is still installed
as before. This allows configure to generate variables which are
specific to the build process and are not intended to be exported to the
installation.
Instead of hand-written INSTALL_{DATA,PROG}. autoconf "does the right
thing" to provide a command which works as expected.

See
  https://www.gnu.org/software/autoconf/manual/autoconf-2.69/html_node/Particular-Programs.html#index-AC_005fPROG_005fINSTALL-269
When building for the first time, the only requirement is that generated
header files have been built (jumptbl.h, version.h and opnames.h).
Detailed dependency information is only required when headers have been
edited.

COMPUTE_DEPS in Makefile.config controls whether C dependency
information should be generated on a per-file basis. This variable is
controlled by a new --disable-dependency-generation in configure which
is enabled for Git checkouts and disabled for tarballs (i.e. releases).

The Microsoft C compiler (cl) cannot generate dependencies in a
consistent way which we can consume, so for a Git checkout configure
searches for an additional C compiler in order to compute dependencies.
This is obviously not required for a user-build.

As a result, the MSVC port can now safely run make alldepend, since only
OCaml dependency information is committed to the repo after this change.

CI does not need to waste time testing the dependency information,
because it only tests a single build. A single Travis job has been added
which tests the build system code to generate the dependency information
(and provides a single `make -j` run in CI, although Inria's CI also
tests parallel building continuously).
@dra27
Copy link
Member Author

dra27 commented Apr 17, 2020

Thanks, @shindere! I've rebased (to fix the conflicts) but put the changes in the 9 most recent commits. Answers to your points:

  • The .dep/Makefiles were the result of a mistaken earlier iteration where I thought I could use wildcards, but not variables, in an include statement. They're now gone - the .d files get included directly
  • The change from _b.o to .b.o is not in any way related to the way $(DEPSUFFIX) was defined, and I don't think it's possible to keep the _ version without duplicating the rules in runtime/Makefile and otherlibs/systhreads/Makefile. The problem is that you have a series of files which depend on the same C file - e.g. extern_b.o, extern_bd.o, extern_n.o etc. Each one of those depends on its own .d file. All of them depend on extern.c. I can easily generate an empty @ rule which allows make to believe that extern_b.o depends on extern_b.c which depends on extern.c (that's done in COMPILE_C_FILE), but the problem is how I convert extern_b.d in the pattern rule to extern.c - if it's one suffix, then I can use $(subst or whatever with %_b.d, but as far as I'm aware there is no way in make to remove _%.d from a string as that pattern says to make that the strings begin with _ and end with .d and you definitely can't write %_%.d. However, $(basename can simulate what we need, because it removes a generic extension from a filename (if it has one) and returns the filename.
  • The commit message you refer to is correct - I'm not talking about generating the dependencies, I'm talking about compilation, which does require those files to exist (I'm explaining why dependency information is not required to build the object files the first time)
  • Thanks for the catch in configure.ac - I hadn't spotted that message was wrong!
  • I don't think you had suggested $(DEPDIR), but I've done it!
  • As I said, $(DEPSUFFIX) has nothing to do with the change in object names, but I have removed the . from it - in fact by changing it to .$(D) it looks more consistent where it appears in rules next to .$(O)
  • Those headers variables went through a few iterations - given that the variable with all five headers was only used in one place, I've gone with $(GENERATED_HEADERS) for the headers generated by runtime/Makefile (I avoid the word "compiled" because a compiled header in C/C++ is a Microsoft "thing") and $(CONFIG_HEADERS) for the ones generated by configure. Neither variable includes the other.
  • The domain_state*.inc is definitely part of this PR - it gets altered because version.h which was on that line is deleted via $(GENERATED_HEADERS).
  • I often develop with --disable-native-compiler because you can quickly test most of the build system with make -j world - I probably then ran make allopt in runtime and found the message very confusing. I can drop it from here, but I'm not assembling an entire just for that! It's part of this PR inasmuch as runtime/Makefile has a lot of native/byte specific dependency stuff to test...
  • I initially thought that using the _OBJECTS would be neater, but it doesn't work - the native code ones include the assembly objects, which obviously don't have dependencies or at least C files. I think what you were hoping to achieve shouldn't be done - all dependency files should be generated all the time - configuring with --disable-debug-runtime stops make all from building it, but it shouldn't prevent a core developer from being able to say make -C runtime libcamlrund.a, so you can't predict which dependency information is needed unless you analyse the targets requested. I think that can be exceptionally to make things like make distclean work without configuring, but for actual build targets I think it runs a risk of being brittle and annoying if it ends up wrong? Incidentally, that's why --disable-native-compiler does and should turn off the dependency of the native dependency files - none of those things can actually be compiled if you configured with that turned off, unlike the various runtimes.

@shindere
Copy link
Contributor

shindere commented Apr 17, 2020 via email

@dra27
Copy link
Member Author

dra27 commented Apr 17, 2020

No problem - have a good weekend, @shindere!

@shindere
Copy link
Contributor

shindere commented Apr 17, 2020 via email

@shindere
Copy link
Contributor

shindere commented Apr 20, 2020 via email

@dra27 dra27 force-pushed the ocaml-dyndepend branch 2 times, most recently from 7aa6686 to 68a68b9 Compare April 20, 2020 09:36
@dra27
Copy link
Member Author

dra27 commented Apr 20, 2020

My only reason to wait on this one was because I was implementing a suggestion I had made in the other PR, rather than giving it a chance to be resolved!

Final rebase done, this should be ready to go!

@gasche
Copy link
Member

gasche commented Apr 20, 2020

I have an unrelated question, but: did you check with existing heavy-Windows-users (other than David of course), for example @nojb, that they were happy with the restriction, in the MSCV port, of having another C compiler available on the development machines?

@dra27
Copy link
Member Author

dra27 commented Apr 20, 2020

Yes, but only inasmuch as Nicolás was part of the thread on caml-devel which started this PR.

Just to be totally clear (FTR as much as anything else) - this only adds a requirement to a developer of the compiler and one who works on the runtime itself - it doesn't affect you if you distribute OCaml, even if you wish to build OCaml from source on MSVC as part of that. It's also now the case that incremental compilation always works, even on MSVC, it just massively over-rebuilds C files if you don't have the mingw compiler (that wasn't the case in the first version of the code here).

@nojb
Copy link
Contributor

nojb commented Apr 20, 2020

Just to be totally clear (FTR as much as anything else) - this only adds a requirement to a developer of the compiler and one who works on the runtime itself - it doesn't affect you if you distribute OCaml, even if you wish to build OCaml from source on MSVC as part of that. It's also now the case that incremental compilation always works, even on MSVC, it just massively over-rebuilds C files if you don't have the mingw compiler (that wasn't the case in the first version of the code here).

Sorry, I hadn't followed the details, but indeed what is described here seems perfectly OK: there is not actually dependence on the mingw compiler, but without it, you get a "one C file changed -> rebuild all C objects" behavior.

@dra27
Copy link
Member Author

dra27 commented Apr 20, 2020

Not quite: without the mingw compiler you get a "one H file changed -> rebuild all C objects". Changing a C file still only recompiles that one C file.

@nojb
Copy link
Contributor

nojb commented Apr 20, 2020

Not quite: without the mingw compiler you get a "one H file changed -> rebuild all C objects". Changing a C file still only recompiles that one C file.

Indeed, that's what I had in mind ("dune behaviour")

@dra27
Copy link
Member Author

dra27 commented Apr 30, 2020

Right, 9082 is in, although in the end this didn't conflict much with it. Assuming CI passes, I shall merge.

@shindere
Copy link
Contributor

shindere commented Apr 30, 2020 via email

@dra27
Copy link
Member Author

dra27 commented Apr 30, 2020

The term “staycation” got coined for this in the financial crisis when people couldn’t afford to go away!

@shindere
Copy link
Contributor

shindere commented Apr 30, 2020 via email

@dra27 dra27 merged commit e5c8bee into ocaml:trunk May 1, 2020
@dra27 dra27 deleted the ocaml-dyndepend branch May 1, 2020 06:36
@gasche
Copy link
Member

gasche commented May 1, 2020

There is a bug in this PR caused by a missing -MG that breaks alldepend for otherlibs/win32unix. @dra27 and myself hit it while trying to rebase #8753 (the CI test that alldepend is a no-op). @dra27 proposed a fix which is to add -MG in otherlibs/Makefile.otherlibraries.common. He is working on a more general fix (I suggested a DEP_CC_FLAGS variable used consistently.)

@dra27 dra27 mentioned this pull request May 3, 2020
gretay-js added a commit to gretay-js/flambda-backend that referenced this pull request Aug 18, 2021
gretay-js added a commit to gretay-js/flambda-backend that referenced this pull request Aug 18, 2021
mshinwell pushed a commit to ocaml-flambda/flambda-backend that referenced this pull request Aug 23, 2021
* Remove hack for .depend in runtime/dune

Port and extend flambda-backend/ocaml#460

* Remove reference to runtime/.depend from runtime/dune

runtime/.depend removed by ocaml/ocaml#9332
poechsel pushed a commit to poechsel/flambda-backend that referenced this pull request Sep 3, 2021
* Remove hack for .depend in runtime/dune

Port and extend flambda-backend/ocaml#460

* Remove reference to runtime/.depend from runtime/dune

runtime/.depend removed by ocaml/ocaml#9332
poechsel pushed a commit to ocaml-flambda/ocaml that referenced this pull request Sep 3, 2021
* Remove hack for .depend in runtime/dune

Port and extend flambda-backend/ocaml#460

* Remove reference to runtime/.depend from runtime/dune

runtime/.depend removed by ocaml#9332
poechsel pushed a commit to ocaml-flambda/flambda-backend that referenced this pull request Sep 20, 2021
* Remove hack for .depend in runtime/dune

Port and extend flambda-backend/ocaml#460

* Remove reference to runtime/.depend from runtime/dune

runtime/.depend removed by ocaml/ocaml#9332
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

5 participants