How to Compile Universal Binaries!!

Mac OSX specific forum
lexvictory
Addict
Addict
Posts: 1027
Joined: Sun May 15, 2005 5:15 am
Location: Australia
Contact:

How to Compile Universal Binaries!!

Post by lexvictory »

As many of you know, this is not possibly natively in PB, so I created a tool that does it!
It can run as a IDE Tool (you need to manually put in the full path to the unix exe), or you can put it in the dock and drag your pb files to it.
It reads the compiler options from the end of the source file, then gets you to confirm and then hit compile. Simple!

Unfortunately, I don't think it will work with IDE defined constants (compile count and the like) - but I haven't tested this!
Only works with .app bundles. (non bundles are easy enough to do in terminal)

Requirements: Intel Mac (Intel PB won't run on a PPC Mac ;))
Source and binary available: http://demonioardente.us.to/pb/PBUniversalCompile.dmg
If you compile it yourself you will have to copy the Info.plist into the .app bundle after compilation if you wish to drag source files to the dock icon.

Make sure you copy the bundle somewhere where your user account can write to; it writes a .prefs file inside it's bundle

Let me know if you have any improvements/bugs!
(All icons used are copyright their respective owners)
Last edited by lexvictory on Wed Mar 11, 2009 12:18 pm, edited 1 time in total.
Demonio Ardente

Currently managing Linux & OS X Tailbite
OS X TailBite now up to date with Windows!
jamirokwai
Enthusiast
Enthusiast
Posts: 796
Joined: Tue May 20, 2008 2:12 am
Location: Cologne, Germany
Contact:

Re: How to Compile Universal Binaries!!

Post by jamirokwai »

lexvictory wrote:As many of you know, this is not possibly natively in PB, so I created a tool that does it!
Wow. Cute!
I can't test it though on ppc because I only own a MacBook, but you may have saved me a lot of time.
If there is anybody out there using a ppc-machine, please test my tool from
http://www.quadworks.de/download.php?ar ... nhance.zip

If it works, I would recommend your little tool to everyone coding on Mac OS X, lexvictory!!!
Thank you very much!
Regards,
JamiroKwai
lexvictory
Addict
Addict
Posts: 1027
Joined: Sun May 15, 2005 5:15 am
Location: Australia
Contact:

Post by lexvictory »

:D
Yes, definitely someone test it - I don't have a PPC machine to test on either
Demonio Ardente

Currently managing Linux & OS X Tailbite
OS X TailBite now up to date with Windows!
lexvictory
Addict
Addict
Posts: 1027
Joined: Sun May 15, 2005 5:15 am
Location: Australia
Contact:

Post by lexvictory »

ok, just tested with arch -ppc in terminal, and it seems to start rosetta (hard drive noise and long startup time) and does execute the PPC code
(my test program while developing this was a one liner with a messagerequester showing #PB_Compiler_Processor)
Demonio Ardente

Currently managing Linux & OS X Tailbite
OS X TailBite now up to date with Windows!
jamirokwai
Enthusiast
Enthusiast
Posts: 796
Joined: Tue May 20, 2008 2:12 am
Location: Cologne, Germany
Contact:

Re: How to Compile Universal Binaries!!

Post by jamirokwai »

jamirokwai wrote:
lexvictory wrote:As many of you know, this is not possibly natively in PB, so I created a tool that does it!
Wow. Cute!
I can't test it though on ppc because I only own a MacBook, but you may have saved me a lot of time.
If there is anybody out there using a ppc-machine, please test my tool from
http://www.quadworks.de/download.php?ar ... nhance.zip

If it works, I would recommend your little tool to everyone coding on Mac OS X, lexvictory!!!
Thank you very much!
Hm. A friend of mine owns a G5 iMac. My little tool from above does not run on this machine.
Either this is not working because of errors in my code, or there are other issues...
Regards,
JamiroKwai
lexvictory
Addict
Addict
Posts: 1027
Joined: Sun May 15, 2005 5:15 am
Location: Australia
Contact:

Post by lexvictory »

try it with a simple thing - like a messagerequester that shows a different message based on what processor it is running on.
only then can you isolate if it is a programming error, or this tool's fault.
Demonio Ardente

Currently managing Linux & OS X Tailbite
OS X TailBite now up to date with Windows!
User avatar
DoubleDutch
Addict
Addict
Posts: 3220
Joined: Thu Aug 07, 2003 7:01 pm
Location: United Kingdom
Contact:

Re: How to Compile Universal Binaries!!

Post by DoubleDutch »

Can you post the source to this tool, the download links are no longer working.
https://deluxepixel.com <- My Business website
https://reportcomplete.com <- School end of term reports system
User avatar
J. Baker
Addict
Addict
Posts: 2181
Joined: Sun Apr 27, 2003 8:12 am
Location: USA
Contact:

Re: How to Compile Universal Binaries!!

Post by J. Baker »

DoubleDutch wrote:Can you post the source to this tool, the download links are no longer working.
Give App Chef a try...
http://www.forums.purebasic.com/english ... 27&t=57491
www.posemotion.com

PureBasic Tools for OS X: PureMonitor, plist Tool, Data Maker & App Chef


Even the vine knows it surroundings but the man with eyes does not.
User avatar
DoubleDutch
Addict
Addict
Posts: 3220
Joined: Thu Aug 07, 2003 7:01 pm
Location: United Kingdom
Contact:

Re: How to Compile Universal Binaries!!

Post by DoubleDutch »

Thanks, but I want to do create the universal binary of the two OSX files on Windows once they have been injected with some data, that's why I needed the source. I also wanted to know if I could add my own common data segment into the combined executable that they could both share.
https://deluxepixel.com <- My Business website
https://reportcomplete.com <- School end of term reports system
lexvictory
Addict
Addict
Posts: 1027
Joined: Sun May 15, 2005 5:15 am
Location: Australia
Contact:

Re: How to Compile Universal Binaries!!

Post by lexvictory »

I can't find the file at the moment - I used a separate folder for the mac stuff.

From what I remember though it basically just compiled the program (once in Intel, once in PPC), then used a command line program (that should be in Xcode stuff) to combine it into the one .app bundle/folder. You can possibly find the name of the command on the forum here, or definitely on google.

I don't believe there's a way to do it on windows however.

As for a common data segment you'd likely be best to put it inside the Resources folder in the bundle; I'm not sure that it combines them into one physical binary file (I think there's still 2 binaries inside the bundle), but it's been a while and I don't know how that works with datasections.

I'll still try find that program, but it won't tell you much more than that.

EDIT: aaaand just as I post that i found the folder!
Demonio Ardente

Currently managing Linux & OS X Tailbite
OS X TailBite now up to date with Windows!
lexvictory
Addict
Addict
Posts: 1027
Joined: Sun May 15, 2005 5:15 am
Location: Australia
Contact:

Re: How to Compile Universal Binaries!!

Post by lexvictory »

Code: Select all

;written thanks to http://developer.apple.com/technotes/tn2005/tn2137.html
;thanks to freak @ http://purebasic.fr/english/viewtopic.php?t=34231 for the drag to dock icon functinality.

#eventNotHandledErr = -9874

#CoreEventClass = 'aevt'
#AEOpenDocuments = 'odoc'
 
#typeFSS = 'fss '
#typeAEList = 'list'
#typeFSRef = 'fsrf'
#keyDirectObject = '----'

#typeNull = 'null'
#noErr = 0

Structure FSRef
  hidden.b[80]
EndStructure

Structure AEDesc
  desctiptorType.l
  dataHandle.l
EndStructure 

Declare OpenTheWindow(pbsrc.s)

ProcedureCDLL OpenDocument(*appleEvt, *reply, refcon)
 
  ; extract the event parameters
  If AECreateDesc_(#typeNull, 0, 0, @documents.AEDesc) = #noErr
    If AEGetParamDesc_(*appleEvt, #keyDirectObject, #typeAEList, @documents)  = #noErr
      If AECountItems_(@documents, @n) = 0

        *Buffer = AllocateMemory(1000)
        If *Buffer

          ; There can be multiple files sent
          For i = 1 To n
            If AEGetNthPtr_(@documents, i, #typeFSRef, @keyWd, @typeCd, @fileRef.FSRef, SizeOf(FSRef), @actualSize) = #noErr
              If FSRefMakePath_(@fileRef, *Buffer, 1000) = #noErr

                File$ = PeekS(*Buffer, -1, #PB_Ascii)
                If FileSize(File$) >= 0 And FindString(LCase(File$), ".pb", 1)
                  OpenTheWindow(File$)
                EndIf
               
              EndIf
            EndIf
          Next i
       
          FreeMemory(*Buffer)
        EndIf
       
      EndIf
    EndIf
    AEDisposeDesc_(@documents)
  EndIf
 
EndProcedure

Procedure.s BundleFilename()
  Protected exe.s = ProgramFilename()
  Protected contentsindex = FindString(exe, "/Contents/MacOS",1)
  ProcedureReturn Left(exe, contentsindex)
EndProcedure

Global PB_Intel_Path.s , PB_PPC_Path.s



Procedure Error(error.s)
  MessageRequester("Error", error)
EndProcedure

Procedure WaitUntilWindowIsClosed()
  Repeat
  Until WaitWindowEvent()= #PB_Event_CloseWindow
  ProcedureReturn 1
EndProcedure

;compileroptions:
Global useunicode, usethread, useonerror, subsystem.s, lastexe.s, icon.s, useinlineasm
Enumeration;gadgets
#pbsrc
#onerror
#icon
#iconbrowse
#lastexe
#subsystem
#unicodemode
#threadmode
#useinlineasm
#compile
#log
EndEnumeration

Procedure DisableGadgets(state)
  DisableGadget(#pbsrc,state)
  DisableGadget(#onerror,state)
  DisableGadget(#icon,state)
  DisableGadget(#iconbrowse,state)
  DisableGadget(#lastexe,state)
  DisableGadget(#subsystem,state)
  DisableGadget(#unicodemode,state)
  DisableGadget(#threadmode,state)
  DisableGadget(#useinlineasm,state)
  DisableGadget(#compile,state)
EndProcedure


Procedure OpenTheWindow(pbsrc.s)
If ReadFile(0, pbsrc)
  While Eof(0)=0
    line.s = ReadString(0)
    If FindString(line, "; IDE Options =",1)
      inoptionssection = 1
    ElseIf inoptionssection
      key.s = StringField(line, 2, " ")
      ;Debug "key: "+key
      Select Trim(key)
        Case "EnableOnError"
          useonerror = 1
        Case "UseIcon"
          valindex = FindString(line, "=", 1)+2
          icon = Mid(line, valindex)
        Case "Executable"
          valindex = FindString(line, "=", 1)+2
          lastexe = Mid(line, valindex)
        Case "EnableUnicode"
          useunicode = 1
        Case "EnableThread"
          usethread = 1
        Case "EnableAsm"
          useinlineasm = 1
        Case "SubSystem"
          valindex = FindString(line, "=", 1)+2
          subsystem = Mid(line, valindex)
      EndSelect
      ;valindex = FindString(line, "=", 1)+2
      ;val.s = Mid(line, valindex)
      ;Debug val
    EndIf
  Wend
EndIf



If lastexe = "" : lastexe=Left(pbsrc, Len(pbsrc)-3)+".app" : EndIf

OpenWindow(0, 0,0, 600, 700,"Confirm Compiler Options - PB Universal Binary Creator", #PB_Window_ScreenCentered|#PB_Window_SystemMenu )
TextGadget(#PB_Any, 1,3,70,20,"PB Source:"): StringGadget(#pbsrc, 72, 1, 525, 20, pbsrc)
TextGadget(#PB_Any, 1, 28, 70, 20,"Output to:") : StringGadget(#lastexe, 72, 26, 525, 20, lastexe)
TextGadget(#PB_Any, 1, 54,30, 20, "Icon:") : StringGadget(#icon, 32, 52, 498, 20, icon) : ButtonGadget(#iconbrowse, 535, 52, 60, 20, "...")
TextGadget(#PB_Any, 1, 80, 70, 20, "Subsystem:") : StringGadget(#subsystem, 72, 78, 525, 20, subsystem)
CheckBoxGadget(#onerror, 1, 100, 400, 20, "Enable OnError Lines support")
SetGadgetState(#onerror, useonerror)
CheckBoxGadget(#unicodemode, 1, 125, 400, 20, "Compile in Unicode mode")
SetGadgetState(#unicodemode, useunicode)
CheckBoxGadget(#threadmode, 1, 150, 400, 20, "Compile in ThreadSafe mode")
SetGadgetState(#threadmode, usethread)
CheckBoxGadget(#useinlineasm, 1, 175, 400,20, "Enable Inline ASM support")
SetGadgetState(#useinlineasm, useinlineasm)
ButtonGadget(#compile, 250, 200, 100, 20, "Compile!")
ListViewGadget(#log, 1,230, 598, 468)
AddGadgetItem(#log, -1, "["+FormatDate("%hh:%ii:%ss", Date())+"] Loaded source code: "+pbsrc)

EndProcedure

If OpenPreferences(BundleFilename()+"Contents/Resources/pblocs.prefs")
  PB_Intel_Path.s = ReadPreferenceString("pb_intel", "")
  PB_PPC_Path.s = ReadPreferenceString("pb_ppc", "")
EndIf

writeout = 0

If PB_Intel_Path = ""
  MessageRequester("Select PB Intel Folder", "I need to know where you PB Intel (x86) folder is, please input it into the requester that follows")
  PB_Intel_Path = PathRequester("Select PB Intel (x86) folder.", "/Applications/PureBasic/")
  If PB_Intel_Path = ""
    MessageRequester("Error", "No PB Intel path was supplied, exiting")
    End
  EndIf
  writeout = 1
EndIf
If PB_PPC_Path = ""
  MessageRequester("Select PB PPC Folder", "I need to know where you PB PPC (PowerPC) folder is, please input it into the requester that follows")
  PB_PPC_Path = PathRequester("Select PB PowerPC (PPC) folder.", "/Applications/PureBasicPPC/")
  If PB_PPC_Path = ""
    MessageRequester("Error", "No PB PPC path was supplied, exiting")
    End
  EndIf
  writeout = 1
EndIf

If writeout
  If FileSize(BundleFilename()+"Contents/Resources") = -1
    CreateDirectory(BundleFilename()+"Contents/Resources")
  EndIf
  If CreatePreferences(BundleFilename()+"Contents/Resources/pblocs.prefs")
    WritePreferenceString("pb_intel", PB_Intel_Path)
    WritePreferenceString("pb_ppc", PB_PPC_Path)
    ClosePreferences()
  Else
    MessageRequester("Warning", "Could not save PB locations, you will be prompted again on next start")
  EndIf
EndIf

If #PB_Compiler_Debugger = 0
  SetCurrentDirectory(BundleFilename())
EndIf
  
Global currdir.s = GetCurrentDirectory()
If FileSize(ProgramParameter(0)) > 0 And FindString(LCase(ProgramParameter(0)), ".pb", 1)
  pbsrc.s = ProgramParameter(0)
  OpenTheWindow(pbsrc)
EndIf

OpenWindow(123, 0,0,0,0,"Universal PB Compiler", #PB_Window_Invisible)
If AEInstallEventHandler_(#CoreEventClass, #AEOpenDocuments, @OpenDocument(), 0, 0) <> #NoErr
  MessageRequester("Warning", "Could not register handler for dragging")
EndIf 

Repeat
  event = WaitWindowEvent()
  Select event
    Case #PB_Event_CloseWindow
      ;quit = 1
      CloseWindow(0)
    Case #PB_Event_Menu
      If EventMenu() = #PB_Menu_Quit
        ;Debug "quit pushed!"
        quit = 1
      EndIf
    Case #PB_Event_Gadget
      If EventGadget() = #iconbrowse
        newicon.s = OpenFileRequester("Select .icns file", currdir, "*.icns", 0)
        If newicon
          SetGadgetText(#icon, newicon)
        EndIf
      ElseIf EventGadget() = #compile
        DisableGadgets(1)
        AddGadgetItem(#log, -1, "["+FormatDate("%hh:%ii:%ss", Date())+"] Start Universal compile")
        SetCurrentDirectory(GetPathPart(GetGadgetText(#pbsrc)))
        currdir = GetCurrentDirectory()
        compileroptions.s = ""
        If GetGadgetText(#icon) <> "" : compileroptions= "-n "+#DQUOTE$+GetGadgetText(#icon)+#DQUOTE$ : EndIf
        If GetGadgetText(#subsystem)<> "" : compileroptions + " -s "+#DQUOTE$+GetGadgetText(#subsystem)+#DQUOTE$ : EndIf
        If GetGadgetState(#onerror) : compileroptions + " -l" : EndIf
        If GetGadgetState(#unicodemode) : compileroptions + " -u" : EndIf
        If GetGadgetState(#threadmode) : compileroptions + " -t" : EndIf
        If GetGadgetState(#useinlineasm) : compileroptions + " -i" : EndIf
        lastexe = GetGadgetText(#lastexe)
        ;compileroptions + " -e "+#DQUOTE$+GetGadgetText(#lastexe)+#DQUOTE$+" "+#DQUOTE$+GetGadgetText(#pbsrc)+#DQUOTE$
        ;AddGadgetItem(#log, -1, compileroptions)
        AddGadgetItem(#log, -1, "["+FormatDate("%hh:%ii:%ss", Date())+"] Compiling Intel mode")
        SetEnvironmentVariable("PUREBASIC_HOME", PB_Intel_Path);in case we're not running as a PB tool.
        compiler = RunProgram(PB_Intel_Path+"compilers/pbcompiler", compileroptions+ " -e "+#DQUOTE$+lastexe+#DQUOTE$+" "+#DQUOTE$+GetGadgetText(#pbsrc)+#DQUOTE$, currdir, #PB_Program_Open|#PB_Program_Read|#PB_Program_Hide)
        If compiler
          While ProgramRunning(compiler)
            If AvailableProgramOutput(compiler)
              ;Debug "output available"
              compileroutput.s = ReadProgramString(compiler)
              If FindString(LCase(compileroutput), "error",0)
                wasanerror = 1
                Error(compileroutput)
              ElseIf compileroutput = "- Feel the ..PuRe.. Power -"
                Break;out of the while loop
              EndIf
              AddGadgetItem(#log,-1, "["+FormatDate("%hh:%ii:%ss", Date())+"] "+compileroutput)
            Else
              While WindowEvent()
              Wend
              Delay(20)
            EndIf
          Wend
          CloseProgram(compiler)
          ;-PPC compile
          AddGadgetItem(#log, -1, "["+FormatDate("%hh:%ii:%ss", Date())+"] Compiling PPC mode")
          SetEnvironmentVariable("PUREBASIC_HOME", PB_PPC_Path)
          compiler = RunProgram(PB_PPC_Path+"compilers/pbcompiler", compileroptions+ " -e "+#DQUOTE$+ReplaceString(lastexe, ".app", "-ppc.app", #PB_String_NoCase)+#DQUOTE$+" "+#DQUOTE$+GetGadgetText(#pbsrc)+#DQUOTE$, currdir, #PB_Program_Open|#PB_Program_Read|#PB_Program_Hide)
          If compiler
            While ProgramRunning(compiler)
              If AvailableProgramOutput(compiler)
                ;Debug "output available"
                compileroutput.s = ReadProgramString(compiler)
                If FindString(LCase(compileroutput), "error",0)
                  wasanerror = 1
                  Error(compileroutput)
                ElseIf compileroutput = "- Feel the ..PuRe.. Power -"
                  Break;out of the while loop
                EndIf
                AddGadgetItem(#log,-1, "["+FormatDate("%hh:%ii:%ss", Date())+"] "+compileroutput)
              Else
                While WindowEvent()
                Wend
                Delay(20)
              EndIf
            Wend
            CloseProgram(compiler)
            baseappexe.s = lastexe+"/Contents/MacOS/"+RemoveString(GetFilePart(lastexe), ".app", #PB_String_NoCase)
            ppcappexe.s = ReplaceString(lastexe, ".app", "-ppc.app", #PB_String_NoCase)+"/Contents/MacOS/"+RemoveString(GetFilePart(lastexe), ".app", #PB_String_NoCase)+"-ppc"
            RenameFile(baseappexe, baseappexe+"-intel")
            RunProgram("lipo", "-create "+#DQUOTE$+baseappexe+"-intel"+#DQUOTE$+" "+#DQUOTE$+ppcappexe+#DQUOTE$+" -output "+#DQUOTE$+baseappexe+#DQUOTE$, currdir, #PB_Program_Wait)
            If FileSize(baseappexe) < 0;lipo failure
              error("Merging binaries with lipo failed")
              AddGadgetItem(#log,-1, "["+FormatDate("%hh:%ii:%ss", Date())+"] Universal compile Failed!")
              DisableGadgets(0)
              Continue
            EndIf
            DeleteFile(lastexe+"/Contents/MacOS/"+RemoveString(GetFilePart(lastexe), ".app", #PB_String_NoCase)+"-intel")
            DeleteDirectory(ReplaceString(lastexe, ".app", "-ppc.app"), "", #PB_FileSystem_Recursive|#PB_FileSystem_Force)
          Else
            error("Could not run PPC pbcompiler")
            DisableGadgets(0)
            Continue
          EndIf
        Else
          error("Could not run Intel pbcompiler")
          DisableGadgets(0)
          Continue
        EndIf
        AddGadgetItem(#log,-1, "["+FormatDate("%hh:%ii:%ss", Date())+"] Universal compile done")
        DisableGadgets(0)
      EndIf
  EndSelect
Until quit = 1
Demonio Ardente

Currently managing Linux & OS X Tailbite
OS X TailBite now up to date with Windows!
lexvictory
Addict
Addict
Posts: 1027
Joined: Sun May 15, 2005 5:15 am
Location: Australia
Contact:

Re: How to Compile Universal Binaries!!

Post by lexvictory »

apple appear to have changed their site so that apple link doesnt work. try http://web.archive.org/web/200901111958 ... n2137.html

It seems they do get put into the one file, but it appears to just be a container format.
Demonio Ardente

Currently managing Linux & OS X Tailbite
OS X TailBite now up to date with Windows!
User avatar
DoubleDutch
Addict
Addict
Posts: 3220
Joined: Thu Aug 07, 2003 7:01 pm
Location: United Kingdom
Contact:

Re: How to Compile Universal Binaries!!

Post by DoubleDutch »

Thanks, I'll take a looka nd see what is generated. :)
https://deluxepixel.com <- My Business website
https://reportcomplete.com <- School end of term reports system
Post Reply