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

Add -noruntime option #2309

Merged
merged 2 commits into from Jun 19, 2019

Conversation

@TheLortex
Copy link
Contributor

commented Mar 11, 2019

This behaves almost the same as -runtime-variant, but it is more flexible as it takes a file path as an input instead of a suffix.

@nojb

This comment has been minimized.

Copy link
Contributor

commented Mar 11, 2019

The name is wrong, since this option does not take a path as argument, but rather a "link flag" (-l...).

More generally, what is the use-case for this flag? At first sight it seems too specific to deserve its own command line flag.

Even more generally, I find the proliferation of command line flags (currently 144 for ocamlopt) worrying, and I feel we should do something to rationalize them.

@TheLortex

This comment has been minimized.

Copy link
Contributor Author

commented Mar 11, 2019

I agree with you. The problem is that when compiling with -output-complete-obj I'd like to be able to choose which libasmrun.a is linked. Currently the -runtime-variant option only allows to specify a suffix (for example 'd' => libasmrund.a) to look for in the ocaml library directory. In my situation, which is compiling Mirage unikernels, I'd like to be able to provide a libasmrun.a that doesn't live in the ocaml default search path.

What can be a nicer way to achieve that ?

@nojb

This comment has been minimized.

Copy link
Contributor

commented Mar 11, 2019

Off the top of my head, shouldn't you use -output-obj and link manually with whatever runtime library you want?

@TheLortex

This comment has been minimized.

Copy link
Contributor Author

commented Mar 11, 2019

-output-obj also doesn't link C library stubs. I need something intermediate between -output-obj and -output-complete-obj as I wouldn't want to provide C stubs libraries by hand.

@nojb

This comment has been minimized.

Copy link
Contributor

commented Mar 11, 2019

If memory serves the compiler will look for the runtime library (eg libasmrun.a) in the load path, so you may want to try passing -I <dir> where <dir> contains "your" runtime library and see if it works.

@nojb

This comment has been minimized.

Copy link
Contributor

commented Mar 11, 2019

(and use -output-complete-obj, of course)

@TheLortex

This comment has been minimized.

Copy link
Contributor Author

commented Mar 11, 2019

I already know about this workaround, but found it better to have a new option for clarity. With the -runtime-variant-path (maybe -runtime-variant-file is better suited) it's clear which version of libasmrun.a will be used. Using -I it might be grabbed somewhere else in the load path.

@nojb

This comment has been minimized.

Copy link
Contributor

commented Mar 11, 2019

I already know about this workaround, but found it better to have a new option for clarity. With the -runtime-variant-path (maybe -runtime-variant-file is better suited) it's clear which version of libasmrun.a will be used. Using -I it might be grabbed somewhere else in the load path.

Personally I don't think this is enough of an argument to justify yet another command line flag. The same kind of ambiguity is present for all other concepts which are searched on lists of include directories, and it doesn't seem to cause much trouble.

@gasche

This comment has been minimized.

Copy link
Member

commented Mar 11, 2019

Maybe one compromise would be to better document the fact that -I is supported wherever -runtime-variant is mentioned, including in the -help output?

(Personally I think that giving a path is a better interface than a filename suffix, so I like the new option better.)

@nojb

This comment has been minimized.

Copy link
Contributor

commented Mar 11, 2019

(Personally I think that giving a path is a better interface than a filename suffix, so I like the new option better.)

We could make the existing flag accept filenames, except when the argument is one of the already recognised suffixes (d, i, p, _pic).

@gasche

This comment has been minimized.

Copy link
Member

commented Mar 11, 2019

I'm not convinced -- these are not enums from a closed world, but just filename suffixes. I suggested to @TheLortex that we behave differently when the "variant" name starts with a slash, but then people want to pass relative paths as well (for example in scripts that should be user-independent), so that doesn't fly.

@nojb

This comment has been minimized.

Copy link
Contributor

commented Mar 11, 2019

OK, what about assuming the argument is a file name, but if the file is not found, try interpreting as a suffix (or the other way around)?

@gasche

This comment has been minimized.

Copy link
Member

commented Mar 11, 2019

Well... personally I like having another option better.

@nojb

This comment has been minimized.

Copy link
Contributor

commented Mar 12, 2019

I still feel it is too specific as a use-case, but won't fight it anymore 😄

@gasche

This comment has been minimized.

Copy link
Member

commented Mar 12, 2019

Would someone else have an opinion?

@dbuenzli

This comment has been minimized.

Copy link
Contributor

commented Mar 12, 2019

If you really want one, I also suspect --output-obj is the answer here. I suspect this request stems from a specific build system limitation rather than a true need.

@mshinwell

This comment has been minimized.

Copy link
Contributor

commented Mar 12, 2019

Having another option seems entirely sensible to me.

@dbuenzli

This comment has been minimized.

Copy link
Contributor

commented Mar 12, 2019

Having another option seems entirely sensible to me.

Could we try to rationalize this a bit before ?

First what does --output-complete-obj do exactly ? It's undocumented in the current manual.

In any case this is for the link step, in that step the OCaml compiler automatically links the runtime system (except if you --output-obj). I would rather have an option that says -noruntime (following -nostlib convention) that prevents any runtime system to be linked and that picks up whatever you specify on the cli for linking as is already the case. In this case you will perfectly be able to provide the full path to the runtime system of your choice on the cli.

@mshinwell

This comment has been minimized.

Copy link
Contributor

commented Mar 12, 2019

I'm unsure why we're bringing the output-obj thing into the discussion. It's perfectly reasonable to want to link a normal executable with a different runtime library without using any of that functionality.

It should certainly be clarified exactly what this proposed option takes as argument.

@dbuenzli 's idea of -no-runtime seems fine too (so long as for a simple link you can specify linker options to ocamlopt or whatever, to pick up the runtime library you want, and those options appear in the correct place on the final link line).

@dbuenzli

This comment has been minimized.

Copy link
Contributor

commented Mar 12, 2019

I'm unsure why we're bringing the output-obj thing into the discussion.

Because currently it's the only option that allows you to by-pass the automatic behaviour of the OCaml link step in order to control the link step yourself, which allows, among other things to specify your own runtime system which is what this PR is about. And you can legitimately ask whether this is not already good enough for people that need to do so.

@shindere

This comment has been minimized.

Copy link
Contributor

commented Mar 12, 2019

@shindere

This comment has been minimized.

Copy link
Contributor

commented Mar 12, 2019

@dbuenzli

This comment has been minimized.

Copy link
Contributor

commented Mar 12, 2019

I would prefer to see if the -noruntime flag is feasible (as @mshinwell mentioned the link order may be problematic).

This allows to understand flags in terms of others. For example if I understood -output-complete-obj correctly you'd have the following equation:

 -output-obj = -output-complete-obj -noautolink -noruntime
@avsm

This comment has been minimized.

Copy link
Member

commented Mar 12, 2019

I don't think enough context was supplied with this PR to adequately judge the tradeoffs suggested above, so let me dive in here with that information before the CLI option popularity contest begins in earnest.

In MirageOS, we compile OCaml source code that has been functorised against the external interfaces it uses for a variety of 'exotic' targets such as standalone Xen, KVM and Muen unikernels that link a boot layer, device drivers and application logic into one bootable kernel. The assembly process involves selecting concrete OCaml driver implementations for a chosen backend, doing a full OCaml library compilation, and then linking the whole lot against a (typically C) bootlayer to build the final executable.

This may sound very exotic, but it's in fact no different from many of the workflows to generate shared libraries or other similar 'non-traditional' outputs from the compiler. So our goal is to remove as many of the custom hacks as we have in Mirage at the moment and use upstream interfaces where possible.

Current Method

Right now, mirage (3.x) builds the OCaml cmx[a] as normal, and generates multiple findlib packages for any C stubs that have been cross-compiled to a particular backend. Each backend (e.g. mirage-solo5) installs a C library via opam that exposes its cross-compilation CFLAGS via pkg-config.

The link process thus involves:

  • ocamlbuild [.cmxa]
  • use -output-obj to generate a .o
  • iterate through the META files of OCaml dependencies to extract .a files for C archives, using ocamlfind predicates to select the right backend (xen_linkopts in the META file)
  • link manually with the bootcode that includes a copy of the ocaml runtime.

Pros/cons:

  • the number of hacks in the build systems of surrounding packages are immense. Exposing a new hardware layer is a lot of work
  • C stubs often have to live outside of the package they were born in
  • manually iterating through the dependency list is code we have to maintain externally and seems unnecessary
  • several opam packages are not coinstallable which necessitates the use of opam switches to build for multiple mirage backends
  • pkg-config is necessary in various places to query CFLAGS, and portability is a pain here

New model

We are now using dune for the majority of our libraries in order to use the fast and composable builds (so that a unikernel is built from a single dune build invocation). We first eliminated some debt by adding virtual_modules into dune to make the classic "linking hack" a first class build feature. This allows us to delay the choice of a concrete archive to link time. This crucially permits us to build multiple OCaml libraries with the same C stubs cross-compiled, so that the C archives can be linked separately, but using the same OCaml cmi.

Then the next stage is for dune to select the right group of virtual modules by means of a tag feature which allows an executable to search for implementations that satisfy an executable's virtual module dependencies. With this, we no longer need the pkg-config and META file hacks above, since we simply embed the C linkopts using the standard OCaml mechanism of stashing them inside the relevant cmxa objinfo.

So given all this, we are at a point in Mirage where the bleeding edge tree has a principled way to track all the cross-compilation needs of Mirage through a very complex build chain. We get to the final link phase, which is where this PR came in. The choices are:

  • use -output-obj: this doesn't link the C dependencies, which will mean we still need the logic for the dependency search. We want to avoid this.
  • use -output-complete-obj: this links in the C dependencies (which are now correctly chosen due to the use of virtual modules) but also the standard runtime. We need to link our own runtime due to the cross-compilation needs, so this wont work.
  • use -output-complete-obj and -runtime-variant: this links in the C dependencies, and lets us specify a custom runtime variant. Initially we thought it wouldn't work with system compilers, but the workaround of specifying -I makes it seem that this is an option that will work for Mirage for 4.08. Yay!

So with this background done, I agree with @nojb that a runtime variant path seems too fine-grained to worth a new CLI option. @dbuenzli's suggestion that output-obj not being used due to a build system limitation also isn't quite right -- we want to use the existing OCaml C linkage system rather than maintain our own as mirage currently does.

I'm in favour of a -noruntime option as a complement of -output-complete-obj since it specifies fairly precisely what we need. But given the workaround of -I, I am also happy that Mirage is unblocked and can use an upstream-friendly mechanism today, so doing nothing is also an option if that works for us @TheLortex ?

@dbuenzli

This comment has been minimized.

Copy link
Contributor

commented Mar 12, 2019

so doing nothing is also an option if that works for us @TheLortex ?

I think adding the -noruntime option rather than relying on -I is better. It is more friendly to build systems.

@alainfrisch

This comment has been minimized.

Copy link
Contributor

commented Mar 12, 2019

@dbuenzli's suggestion that output-obj not being used due to a build system limitation also isn't quite right -- we want to use the existing OCaml C linkage system rather than maintain our own as mirage currently does.

Fair enough, but it could be argued that C dependencies being stored in the .cmxa files and managed by the compiler "automatically" is at odds with the general lack of automatic dependency linking (for OCaml code), and that it's rather the responsibility of the build system (dune or ocamlfind) to manage them as well.

That being said:

  • The current -runtime-variant option is quite exotic. It would seem natural to allow passing an arbitrary file. We only need to maintain backward compatibility, but this should be easy (either fallback to the suffix semantics when the file is not available, or the opposite; or whitelist a list of known suffixes).

  • It also seem in line with other existing options to allow removing the runtime library from the link (and then passing it explicitly with usual flags). If we need to add one option, I'd prefer this one over a new variant of -runtime-variant.

  • If we don't want to add an option nor generalize -runtime-variant to arbitrary file names, one could embrace that -runtime-variant has a weird semantics for its argument, and allow e.g. -runtime-variant none to mean that no runtime library should be automatically linked.

@gasche

This comment has been minimized.

Copy link
Member

commented Mar 12, 2019

Le me veto any weird fallback semantics for -runtime-variant. I didn't want to be so blunt when @nojb suggested it, but this is ugly as hell. I also dislike the "just hardcode a list of shortnames", but this is not covered by the above veto.

@nojb

This comment has been minimized.

Copy link
Contributor

commented Mar 12, 2019

I think the -noruntime option is the best of all alternatives put forward (most orthogonal and with simplest semantics).

@mshinwell

This comment has been minimized.

Copy link
Contributor

commented Mar 12, 2019

I think -noruntime is only fine if there is no link order problem. (We should be able to link a normal executable with a different runtime using a normal ocamlopt invocation that contains something along the lines of -noruntime -cclib -lmyruntime.)

@dbuenzli

This comment has been minimized.

Copy link
Contributor

commented Mar 12, 2019

According to what I see when I invoke

touch a.ml
ocamlopt -verbose -cclib -lc -cclib -lm a.ml
ocamlopt -verbose a.ml -cclib -lc -cclib -lm

this should be alright, the runtime system archive is added at the very end of the cc invocation and so are the -l flags, just before, in the provided order. So -noruntime will simply remove the final path to the runtime archive and all should be well.

@TheLortex

This comment has been minimized.

Copy link
Contributor Author

commented Mar 22, 2019

@damiendoligez I added the option for bytecode compilation: now -noruntime removes the runtime header line when compiling bytecode. In -custom mode it also remove libcamlrun from the final linking step.

@TheLortex TheLortex force-pushed the TheLortex:runtime-variant-path branch from 425b463 to d3251af Mar 25, 2019
Copy link
Contributor

left a comment

I'm interested in using this for #8537, hence the interest in the bytecode version of the flag.

-noruntime clearly conflicts -use-runtime/-use_runtime in bytecode and -runtime-variant in both drivers - driver/main.ml and driver/optmain.ml should display an error if these all appear.

Changes Outdated Show resolved Hide resolved
bytecomp/bytelink.ml Outdated Show resolved Hide resolved
bytecomp/bytelink.ml Outdated Show resolved Hide resolved
@damiendoligez

This comment has been minimized.

Copy link
Member

commented Mar 27, 2019

-noruntime clearly conflicts -use-runtime/-use_runtime in bytecode

Not entirely. -use-runtime x says two things:

  1. compile against the set of primitives present in x
  2. put x instead of the default runtime in the shebang header

(2) is irrelevant if you specify -noruntime but (1) might still be useful.

@dra27

This comment has been minimized.

Copy link
Contributor

commented Mar 27, 2019

@damiendoligez - I had overlooked the primitives loading. I am actually ordering a bike shed at the moment, so forgive this: ocamlc -use-runtime foo -noruntime feels really strange! Should we perhaps be calling it -no-link-runtime and, apropos of your comment in #2082 (comment), have -link-runtime too, which is obviously the default behaviour?

Noting that -noruntime and -runtime-variant do conflict - is it really a problem to have the conflict in -use-runtime and simply require that the caller, in their build system, must manually do runtime -p prims-file and then use -use-prims if they wish to use -no-runtime? i.e. view that -use-runtime has a convenience mode for when you didn't also specify -use-prims, and that convenience is not available if you've chosen -noruntime?

@xavierleroy

This comment has been minimized.

Copy link
Contributor

commented Mar 27, 2019

What's in a name? A -noruntime option by any other name would smell as nonexecutable :-)

@dra27 is correct that for bytecode, the -noruntime option under discussion doesn't mean that there is no runtime system in the output of the ocamlc linker (unless -custom is given, there is no runtime system there already). It means that the output of the linker is not directly executable by the operating system, because it lacks the appropriate shebang line or Win32 launcher.

Does this mean a different name should be given to -noruntime? I don't know.

Does this mean that -noruntime and -use-runtime are compatible? Definitely yes, there's no point in rejecting that combination.

Does this mean that the bytecode "executable" file produced by ocamlc -noruntime should NOT have the executable bit set? I strongly think so.

@dbuenzli

This comment has been minimized.

Copy link
Contributor

commented Mar 27, 2019

Maybe trying to have the same option name for ocamlc and ocamlopt that do such different things is not such a great idea.

If one agrees that -noruntime for ocamlopt is not a great name and that @dra27's suggestion of -no-runtime-link is a better and clearer name. It becomes obvious that -no-runtime-link would be absurd for ocamlc since @xavierleroy mentioned no runtime system is linked in the bytecode output anyways.

@dra27

This comment has been minimized.

Copy link
Contributor

commented Mar 28, 2019

I'm not clear how writing a #! line which links to a runtime (or embedding a bootstrapping which adds a search for a runtime) differs to the point of absurdity from linking with a DLL, which, modulo thunks, etc., records the name of a DLL in the output.

I agree that L308 in bytelink.ml should be updated not to write in executable mode if the header is not written (that could be done without worrying about whether the header is present, or not, since #8537 will make it an error for the header not to be present without this flag)

@damiendoligez

This comment has been minimized.

Copy link
Member

commented Apr 8, 2019

I agree with @dra27 that prepending the #! line (or a custom runtime) is very similar to linking the runtime in a native program.

The option could be called -without-runtime (with a matching -with-runtime) and documented as:

The compiler does not include the runtime system (nor a reference to it) in
the generated program; it must be supplied separately.
@TheLortex TheLortex force-pushed the TheLortex:runtime-variant-path branch from d3251af to 090be90 Apr 17, 2019
@TheLortex

This comment has been minimized.

Copy link
Contributor Author

commented Apr 17, 2019

So I renamed the option according to @damiendoligez's comment and added the man/manual pages.
Now the executable bit is not set when -without-runtime is enabled.

@nojb

This comment has been minimized.

Copy link
Contributor

commented May 6, 2019

@TheLortex where are we with this PR? Is there anything else left to do? If not, could you rebase on trunk so that it can be reviewed again before merging?

@TheLortex TheLortex force-pushed the TheLortex:runtime-variant-path branch from 090be90 to e61263c May 6, 2019
@TheLortex

This comment has been minimized.

Copy link
Contributor Author

commented May 6, 2019

@nojb I don't think I've missed anything from the past reviews, so it can be reviewed again.

man/ocamlc.m Show resolved Hide resolved
@nojb
nojb approved these changes May 6, 2019
Copy link
Contributor

left a comment

LGTM, other than some tiny cosmetic tweaks.

asmcomp/asmlink.ml Outdated Show resolved Hide resolved
bytecomp/bytelink.ml Outdated Show resolved Hide resolved
bytecomp/bytelink.ml Outdated Show resolved Hide resolved
bytecomp/bytelink.ml Outdated Show resolved Hide resolved
driver/main_args.ml Outdated Show resolved Hide resolved
@nojb

This comment has been minimized.

Copy link
Contributor

commented May 6, 2019

@dra27 can you approve your review if you think this is ready for merging?

@dra27
dra27 approved these changes Jun 10, 2019
@dra27

This comment has been minimized.

Copy link
Contributor

commented Jun 10, 2019

@damiendoligez - is this trunk-only or can it go on 4.09?

@TheLortex

This comment has been minimized.

Copy link
Contributor Author

commented Jun 11, 2019

I added a commit according to @nojb's reviews

@damiendoligez

This comment has been minimized.

Copy link
Member

commented Jun 19, 2019

@dra27 It can go to 4.09 if you merge and cherry-pick before next Monday.

@gasche

This comment has been minimized.

Copy link
Member

commented Jun 19, 2019

I'm going to assume that today is in the middle of @dra27 non-standard weekends, and merge myself at the end of the afternoon if nobody stops me -- or beats me to it.

@gasche gasche merged commit 010d94c into ocaml:trunk Jun 19, 2019
2 checks passed
2 checks passed
continuous-integration/appveyor/pr AppVeyor build succeeded
Details
continuous-integration/travis-ci/pr The Travis CI build passed
Details
@gasche

This comment has been minimized.

Copy link
Member

commented Jun 19, 2019

Rebasing on top of 4.09 runs into a small conflict due to the fact that #8622 was not itself included in 4.09. I considered cherry-picking that other patch, but its diff is scary (it regenerates the configure etc.) so I dropped the idea. Someone else should feel free to do it.

gasche added a commit that referenced this pull request Jun 19, 2019
Add -noruntime option

(cherry picked from commit 010d94c)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
You can’t perform that action at this time.