Search code examples
shake-build-system

How to construct rule names according to an external factor?


Till now i had the following code:

shakeArgsWith opts additionalFlags $ \flags targets -> return $ Just $ do
    ...
    -- Get target arch from command line flags
    let givenArch = listToMaybe [v | AFlag v <- flags]
    let arch = fromMaybe defArch givenArch
    ...
    -- Set the main target
    let outName = masterOutName projType toolchain projName

    let mainTgt = (case projType of
                    Binary _ -> "bin"
                    Archive  -> "lib")
                  </> prettyShowArch arch
                  </> show variant
                  </> outName

    -- Set the build directory for the current run
    let buildDir = bldDir </> show toolchain </> prettyShowArch arch </> show variant
    ...
    mainTgt %> \out -> do ...
    ...
    buildDir <//> "*.o" %> \out -> do ...
    ...

Meaning that the names of the rules where constructed according to a command line flag that i was parsing (they contained the arch variable).

So if i gave shake --arch=x64 i was building the main target in bin/x64/Release directory and my intermidiate build files in the tmp/x64/Release folder accordingly.

But now instead of using the command line flag, i want the shared arch variable that is used to construct the rule names to be populated according to the output of some command, for example if i could define some top level action it would be this:

Stdout sout <- quietly $ cmd (EchoStdout False) (EchoStderr False) "gcc -dumpmachine" :: Action (Stdout String)
let foundArch = show (gccTripletToArch sout)

and use the variable foundArch instead of the arch when constructing the mainTgt and buildDir names. Obviously this cannot be done, as even the only top level rule that can be created with the action function returns Rule (). What can i do instead?


Solution

  • I think you should be able to do:

    shakeArgsWith opts additionalFlags $ \flags targets -> do
         Stdout sout <- cmd (EchoStdout False) (EchoStderr False) "gcc -dumpmachine"
         let arch = show (gccTripletToArch sout)
         return $ Just $ do
             let buildDir = bldDir </> show toolchain </> prettyShowArch arch </> show variant
             ...
             buildDir <//> "*.o" %> \out -> do ...
    

    The target patterns in Shake do have to be statically known, as that ensures some important properties with respect to quick rebuilding (you can guarantee a change in one place has predictable effects). However, you can run commands to determine things like arch, compiler version etc. before you create the build script, and bake them in.

    Another viewpoint is that you are dynamically generated a build system based on the arch. Using Shake, as a Haskell EDSL, that is no particular problem, and you arguably were doing that before with the command line.