1

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?

Fr0stBit
  • 1,455
  • 1
  • 13
  • 22

1 Answers1

1

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.

Neil Mitchell
  • 9,090
  • 1
  • 27
  • 85
  • So i suppose that your answer is to detect the arch before i generate the build script (doing normal operations in main before shakeArgsWith)? The problem is that with your snippet i get ```Couldn't match expected type `IO (Stdout String)' with actual type `Action (Stdout String)'```.. I thought of doing the same thing too but with that solution i loose the power of the prebuilt Action functions that shake offers.. – Fr0stBit Nov 13 '15 at 13:36
  • I removed the `quietly` - does it work now? `cmd` is flexible enough to be used in either `Action` or `IO`. When in `Action`, it does a few extra things, integrates with the Shake lint and profiling etc, but works just fine in IO too. – Neil Mitchell Nov 13 '15 at 13:46
  • Well, obviously we need to remove the type specifier in the end (:: Action (Stdout String)) and it works! Will i have any excessive printing without quietly? – Fr0stBit Nov 13 '15 at 13:53
  • Thanks, now fixed - I need a wider monitor! The printing is part of the Action Monad instance for `cmd`, so won't happen with the `IO` version. – Neil Mitchell Nov 13 '15 at 15:15