Search code examples
macosjam

Creating Application Structure


I am trying to create an OS X desktop application using Haiku Jam. The only files I need are:

  • <appname>.app (directory)
  • <appname>.app/Contents (directory)
  • <appname>.app/Contents/Info.plist (file)
  • <appname>.app/Contents/MacOS (directory)
  • <appname.app>/Contents/MacOS/<appname> (executable file; this is the actual built binary)

The problem is that I don’t know the proper Jamfile code to tell Jam to create these files and directories. You’d think I would create an “Application” rule that calls the built-in MkDir and File rules. That was my approach, except it doesn’t run all the commands due to missing DEPENDS commands. Unfortunately, if I add DEPENDS commands that make a NOTFILE target depend on the files and directories needed, I get weird circular-reference errors, rules not being run (possibly due to circular dependencies), etc. What can I do?

(In make, this would be dead simple, because make runs commands in the order they were typed, every single time. I would create a make rule and have it call mkdir and cp repeatedly as appropriate.)


Solution

  • I don't see why the approach with a simple rule shouldn't work:

    rule Application application : infoFile
    {
      local appDir = $(application:BS=.app) ;
      MakeLocate $(appDir) : ... some directory ... ;
      Depends $(appDir) : $(application) $(infoFile) ;
      Application1 $(appDir) : $(application) $(infoFile) ;
    }
    
    actions Application1
    {
      rm -rf $(1)
      mkdir -p $(1)/Contents/MacOS
      cp $(2[1]) $(1)/Contents/MacOS
      cp $(2[2]) $(1)/Contents/Info.plist
    }
    

    The first argument is the executable target (as passed to Main) -- the rule assumes it has the final name. The second argument is the target for the Info.plist (name doesn't matter). I suppose that is some pre-existing file, so you'll need to tell Jam how to find it. Assuming the file is located in the subdirectory of the Jamfile that builds the executable, the interesting part of that Jamfile could look like this:

    Main MyApp : ... ;
    
    local infoFile = [ FGristFiles Info.plist ] ;
    SEARCH on $(infoFile) = $(SEARCH_SOURCE) ;
    Application MyApp : $(infoFile) ;
    

    You'd build with jam -q MyApp.app. If the info file is always named like that and located in the respective subdirectory you can move the respective code into the Application rule and simplify the invocation. Or if you need it only once, you can just as well move all code out of the Application rule and only keep the actions.

    Alternatively you could do all that without even writing any actions. In your Jamfile:

    local application = MyApp ;
    local infoFile = Info.plist ;
    
    local targetDir = ... some directory ... ;
    local applicationDir = [ FDirName $(targetDir) $(application:BS=.app) ] ;
    local contentsDir = [ FDirName $(applicationDir) Contents ] ;
    local macOsDir = [ FDirName $(contentsDir) MacOS ] ;
    
    Main $(application) : ... ;
    MakeLocate $(application) : $(macOsDir) ;
    
    local targetInfoFile = <$(application)-info-plist)>Info.plist ;
    MakeLocate $(targetInfoFile) : $(contentsDir) ;
    File $(targetInfoFile) : [ FGristFiles $(infoFile) ] ;
    
    NotFile $(application)-dir ;
    Depends $(application)-dir : $(application) $(targetInfoFile) ;
    

    The pseudo-target exists so you can do jam -q MyApp-dir. Obviously, if you want to reuse the code, you can move it into a rule. application and infoFile would again be the parameters. Note that the File rule sets SEARCH of the source file to SEARCH_SOURCE, so you'd have to reset it afterward, if you need different behavior.

    Disclaimer: I haven't tested any of the above code, so typos or other small mishaps are quite possible. But in principle things should work like outlined.