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

[RFC] Allow dune to treat targets opaquely. #1547

Merged
3 commits merged into from
Nov 21, 2018
Merged

Conversation

jordwalke
Copy link
Contributor

Summary: Some targets will be generated by external build commands (such
as static resource managers, documentation generators, etc). Often,
those resources must be generated into a directory and the external tool
will manage the contents of that directory.

Dune can properly determine when to invoke the external build tool to
regenerate that directory content, and the external build tool will be
responsible for doing so.

In this sense, Dune can treat directories generated by build commands as
"opaque" resources, almost as if the directory was the tar equivalent of
the directory.

This diff attempts to implement this, but I'd like some feedback.

There is one issue with this implementation which I could use some help
resolving. Even if the mtime does not change for the file, Dune decides
to regenerate the target - merely because it is a directory, and I
cannot find the place in Dune where it decides to do that.

@ghost
Copy link

ghost commented Nov 19, 2018

The idea is essentially to add support for directory targets. This seems fine, especially as we currently do it for odoc but in a not very clean way. When considering the digest of a directory, we should probably consider the digest of the contents of the directory as well. Otherwise, dune will not re-generate it if it is modified.

Regarding the current problem you are seeing, have you tried printing the stats to see if something is changing?

@jordwalke
Copy link
Contributor Author

jordwalke commented Nov 19, 2018

Regarding the current problem you are seeing, have you tried printing the stats to see if something is changing?

Yes, and something is changing the mtime of them - and it's not me. But I think the mtime is being changed because Dune is regenerating it. I don't know why it's regenerating it. Is there any place you can think of where Dune would care that this is a directory vs. file and therefore remove it and/or regenerate it?

When considering the digest of a directory, we should probably consider the digest of the contents of the directory as well. Otherwise, dune will not re-generate it if it is modified.

My understanding was that Dune will regenerate it if its dependencies are modified, right? I think that factoring the contents of the directories would be useful if those directories are inputs to other targets, right? I was even content with only supporting directories at root of dependency trees, because the common case is external systems that generate docs etc. They aren't typically used as inputs to other rules. Thoughts?

@ghost
Copy link

ghost commented Nov 19, 2018

Yes, and something is changing the mtime of them - and it's not me. But I think the mtime is being changed because Dune is regenerating it. I don't know why it's regenerating it. Is there any place you can think of where Dune would care that this is a directory vs. file and therefore remove it and/or regenerate it?

Not that I can think of. It feels like Dune should really care about whether a target is a directory or a file, though I can't think of a place where this matters at the moment.

My understanding was that Dune will regenerate it if its dependencies are modified, right?

Since #1343, it will also regenerate it if the target is modified.

I think that factoring the contents of the directories would be useful if those directories are inputs to other targets, right?

Yes.

I was even content with only supporting directories at root of dependency trees, because the common case is external systems that generate docs etc. They aren't typically used as inputs to other rules. Thoughts?

That's fine as long as we get a proper error message if we try to use such a directory as dependency.

@jordwalke
Copy link
Contributor Author

For example, even if I change the dir_digest function to always return the string of the file path (which should never change), Dune will still rebuild it from scratch. So for some reason being a directory makes Dune think it changed.

@ghost
Copy link

ghost commented Nov 19, 2018

Ah indeed, that's the function remove_old_artifacts in build_system.ml that deletes it.

@ghost
Copy link

ghost commented Nov 19, 2018

This function should be adapted to support directory targets

@jordwalke
Copy link
Contributor Author

I made the change you suggested. It seems to work.

src/utils.ml Outdated
let path = Path.to_string fn in
let stat = Unix.stat path in
let digest =
if stat.st_kind == Unix.S_DIR then
Copy link

Choose a reason for hiding this comment

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

We can use normal equality here instead of physical one:

Suggested change
if stat.st_kind == Unix.S_DIR then
if stat.st_kind = Unix.S_DIR then

src/utils.ml Outdated
if stat.st_kind == Unix.S_DIR then
dir_digest stat
else
Digest.file (Path.to_string fn)
Copy link

Choose a reason for hiding this comment

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

It looks like we could factorise this bit of code between here and refresh

@ghost
Copy link

ghost commented Nov 21, 2018

Thanks, could you sign your commits as per the CONTRIBUTING.md file and add a changelog entry?

We'll need to update dir_digest so that it takes into account the contents of the directory, but this can be done in another PR.

Summary: Some targets will be generated by external build commands (such
as static resource managers, documentation generators, etc). Often,
those resources must be generated into a directory and the external tool
will manage the contents of that directory.

Dune can properly determine when to invoke the external build tool to
regenerate that directory content, and the external build tool will be
responsible for doing so.

In this sense, Dune can treat directories generated by build commands as
"opaque" resources, almost as if the directory was the tar equivalent of
the directory.

This diff attempts to implement this, but I'd like some feedback.

There is one issue with this implementation which I could use some help
resolving. Even if the mtime does not change for the file, Dune decides
to regenerate the target - merely because it is a directory, and I
cannot find the place in Dune where it decides to do that.

Signed-off-by: Jordan Walke <jordojw@gmail.com>
@jordwalke
Copy link
Contributor Author

Suggestions applied, signed off.

@ghost
Copy link

ghost commented Nov 21, 2018

Thanks. js_of_ocaml doesn't seem to build in travis, we need to double check if it builds with master or if the build of jsoo is broken by this PR.

@ghost
Copy link

ghost commented Nov 21, 2018

I had a quick look and couldn't reproduce the error locally. I restarted the build.

@ejgallego
Copy link
Collaborator

I've seen this error in other contexts when composing builds after #1554 , so indeed I have been working with a pre #1554 tree locally.

@ghost
Copy link

ghost commented Nov 21, 2018

@ejgallego do you have a full backtrace? It is truncated in the CI logs

@ejgallego
Copy link
Collaborator

@ejgallego do you have a full backtrace? It is truncated in the CI logs

Let me try to reproduce.

@ejgallego
Copy link
Collaborator

ejgallego commented Nov 21, 2018

For some reason I am not able to reproduce reliably but it looks like this:

Description:
("Build_system.get_collector called on closed directory"
 (dir (In_build_dir install/default/bin))
 (load_dir_stack ()))
Backtrace:
Raised at file "src/stdune/exn.ml", line 32, characters 5-10
Called from file "src/build_system.ml", line 1589, characters 18-47
Called from file "src/install_rules.ml", line 217, characters 6-77
Called from file "list.ml", line 99, characters 22-25
Called from file "src/stdune/list.ml", line 5, characters 19-33
Called from file "src/install_rules.ml", line 321, characters 6-108
Called from file "src/install_rules.ml", line 342, characters 6-22
Called from file "map.ml", line 295, characters 20-25
Called from file "map.ml", line 295, characters 10-18
Called from file "src/gen_rules.ml", line 234, characters 4-25
Called from file "map.ml", line 295, characters 20-25
Called from file "src/gen_rules.ml", line 308, characters 2-60
Called from file "src/fiber/fiber.ml", line 111, characters 22-27
Called from file "src/fiber/fiber.ml", line 299, characters 6-13

@ghost
Copy link

ghost commented Nov 21, 2018

Thanks, I think I see the issue

@ghost
Copy link

ghost commented Nov 21, 2018

Anyway, we can merge this PR since the build failure is due to something else.

@ghost ghost merged commit 009cb16 into ocaml:master Nov 21, 2018
@ghost
Copy link

ghost commented Nov 21, 2018

@jordwalke, thanks for your contribution!

@ejgallego
Copy link
Collaborator

Shouldn't this PR have also updated doc/ ?

@ghost
Copy link

ghost commented Nov 26, 2018

Before advertising this feature more, we need to:

  • implement proper hashing of directory, otherwise depending on a directory will not have the desired effect
  • allow to declare that a target is a directory, so that if a rule A produces directory foo and another rule B requires file foo/bar, then Dune knows that A produces foo/bar.

@ejgallego
Copy link
Collaborator

Cool, thanks @diml, should something be done about (install ...) too?

@ghost
Copy link

ghost commented Nov 26, 2018

Yh, we should be able to install a directory. Also that's quite a bit more work.

@rgrinberg
Copy link
Member

How do we handle $ dune build dir now? Before this would mean just to build $ dune build @dir/all, but now it should mean build the dir target if it exists.

@ghost
Copy link

ghost commented Nov 28, 2018

Having dir both as a target and present in the source tree doesn't seem compatible, so the current behavior of dune build dir is indeed:

  • build directory dir if it is a target
  • build @dir/all if dir is present in the source tree

That said, we should probably add a check somewhere to make sure we don't declare an existing directory as target.

@ghost ghost mentioned this pull request Nov 28, 2018
let ints = stat.Unix.st_size + stat.st_perm in
(string_of_int ints ^
string_of_float stat.st_mtime ^
string_of_float stat.st_ctime)
Copy link
Member

Choose a reason for hiding this comment

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

It's a relatively minor thing, but (a ^ b ^ c) will allocate an intermediate string (a^b) that creates unnecessary garbage. It might be better to do a sprintf here to do it in one allocation if the digest function is called a lot

Copy link

Choose a reason for hiding this comment

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

Now that you point that out, I just realise that we are missing separators in this string. i.e. two different stats records might produce the same string... That's very unlikely, but just to avoid readers wondering about it, we should add separators

Copy link
Member

Choose a reason for hiding this comment

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

Fix here #1592

Copy link
Contributor Author

Choose a reason for hiding this comment

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

thank you all!

rgrinberg added a commit to rgrinberg/opam-repository that referenced this pull request Nov 29, 2018
CHANGES:

- Expand variables in `install` stanzas (ocaml/dune#1354, @mseri)

- Add predicate language support for specifying sub directories. This allows the
  use globs, set operations, and special values in specifying the sub
  directories used for the build. For example: `(dirs :standard \ lib*)` will
  use all directories except those that start with `lib`. (ocaml/dune#1517, ocaml/dune#1568,
  @rgrinberg)

- Add `binaries` field to the `(env ..)` stanza. This field sets and overrides
  binaries for rules defined in a directory. (ocaml/dune#1521, @rgrinberg)

- Fix a crash caused by using an extension in a project without
  dune-project file (ocaml/dune#1535, fix ocaml/dune#1529, @diml)

- Allow `%{bin:..}`, `%{exe:..}`, and other static expansions in the `deps`
  field. (ocaml/dune#1155, fix ocaml/dune#1531, @rgrinberg)

- Fix bad interaction between on-demand ppx rewriters and using multiple build
  contexts (ocaml/dune#1545, @diml)

- Fix handling of installed .dune files when the backend is declared via a
  `dune` file (ocaml/dune#1551, fixes ocaml/dune#1549, @diml)

- Add a `--stats` command line option to record resource usage (ocaml/dune#1543, @diml)

- Fix `dune build @doc` deleting `highlight.pack.js` on rebuilds, after the
  first build (ocaml/dune#1557, @aantron).

- Allow targets to be directories, which Dune will treat opaquely
  (ocaml/dune#1547, @jordwalke)

- Support for OCaml 4.08: `List.t` is now provided by OCaml (ocaml/dune#1561, @ejgallego)

- Exclude the local esy directory (`_esy`) from the list of watched directories
  (ocaml/dune#1578, @andreypopp)

- Fix the output of `dune external-lib-deps` (ocaml/dune#1594, @diml)

- Introduce `data_only_dirs` to replace `ignored_subdirs`. `ignored_subdirs` is
  deprecated since 1.6. (ocaml/dune#1590, @rgrinberg)
@cpitclaudel
Copy link
Contributor

Having dir both as a target and present in the source tree doesn't seem compatible

But what about a promoted directory? Wouldn't that end up in the source tree and as a target?

@ghost
Copy link

ghost commented Nov 23, 2021

FTR, we have been working on proper support for directory targets, including promoted directory targets. The support is almost complete in the main branch. It is still guarded by (using directory-targets 0.1) until the feature is mature enough.

@ghost ghost mentioned this pull request Nov 23, 2021
This pull request was closed.
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.

6 participants