Shake: how do I depend on dynamically generated source files?

303 Views Asked by At

Given a directory structure like this:

.
├── frontend
│   ├── _build/          -- build dir, all files produced by shake, except for Frontend.elm, go here
│   ├── Build.hs         -- the build script
│   ├── build.sh         -- wrap Build.hs with `stack exec build -- $@`
│   ├── other files ...
│   ├── Frontend.elm     -- generated by a rule in Build.hs, `protoc  -I../proto --elm_out=. ../proto/frontend.proto`
│   ├── Index.elm        -- hand written source file
│   └── other elms ...   -- hand written source files
└── proto
    └── frontend.proto   -- protocol buffer message defination, hand written

Target _build/index.js depends on all .elm files, including Frontend.elm, but Frontend.elm is generated by a rule in Build.hs, if I blindly do:

want ["_build/index.js"]
"_build/index.js" %> \out -> do
    elms <- filter (not . elmStuff)
            <$> (liftIO $ getDirectoryFilesIO "" ["//*.elm"])
    need elms
    blah blah

want ["Frontend.elm"]
"Frontend.elm" %> \_out -> do
    cmd ["protoc", "blah", "blah"]

build.sh clean would give me:

Lint checking error - value has changed since being depended upon:
  Key:  Frontend.elm
  Old:  File {mod=0x608CAAF7,size=0x53D,digest=NEQ}
  New:  File {mod=0x608CAB5B,size=0x53D,digest=NEQ}

Is there a way to tell shake to watch out for the dynamically generated Frontend.elm, maybe build it first so it doesn't change during the rest of the build, I tried priority 100 ("Frontend.elm" %> ...), doesn't work.

1

There are 1 best solutions below

8
On

You should probably:

  1. Switch from getDirectoryFilesIO, which does not track changes to the file system, to getDirectoryFiles, which does.
  2. Declare your dependence on Frontend.elm, which you know you need even if it does not exist in the filesystem yet (hence might not be visible to getDirectoryFiles).
  3. (Optional) Don't bother wanting Frontend.elm, since you only wanted it as a hack to enable _build/index.js.

With these changes, it would look like this:

want ["_build/index.js"]
"_build/index.js" %> \out -> do
    need ["Frontend.elm"]
    elms <- filter (not . elmStuff)
            <$> getDirectoryFiles "" ["//*.elm"]
    need elms
    blah blah

"Frontend.elm" %> \_out -> do
    cmd ["protoc", "blah", "blah"]

Caveat lector: I have not tested this solution.