Linux Compilation Request

Linux specific forum
Olby
Enthusiast
Enthusiast
Posts: 461
Joined: Mon Jan 12, 2009 10:33 am
Contact:

Linux Compilation Request

Post by Olby »

Hi guys,

Could someone please help me out and compile this source code into Linux executable. I don't have access to Linux right now thats why I would really appreciate it.

Code: Select all

    ; Copyright: AND51, 2009
    ; Contact: AND51 at purebasic.fr/english
    ; E-Mail: purebasic@and51.de
    ;       You may use this app, there is no special licence.
    ;       Just make me happy by putting me into your credits
    ;    Note: We must circumvent a bug in ProgramRunning() (see lines 142 and 233) (only on Linux without debugger).

    ; inform child processes, such as the PB-scripts, about this programs version
    SetEnvironmentVariable("PB_Interpreter_Version", "150")

    ; to show that we have good manners :-)
    EnableExplicit

    ; for later usage, measures the time needed for whole compilation
    Define creation_time=ElapsedMilliseconds()

    ; open console for apache (this is our stdout)
    If Not OpenConsole()
       End
    EndIf

    ; collect program parameters to pass them to the purebasic-executable later on.
    ; you can define program parameters in the shebang-line. example:
    ; #!/purebasic/interpreter -param1 -w /fast
    ; these params are passed to this interpreter and we forward them to the pb-executable.
    ; some parameters are also relevant for the behaviour of this interpreter.
    ; on my linux server, all parameters are passed as one parameter, so we only catch param number one!
    Define commandline$=RemoveString(RemoveString(ProgramParameter(0), #LF$), #CR$), i, n
    NewList parameters.s()
    If commandline$
       n=CountString(commandline$, " ")+1
       For i=1 To n
          AddElement(parameters())
             parameters()=StringField(commandline$, i, " ")
       Next
    EndIf

    Procedure isParameter(parameter$)
       ; returns 0 unless the specified parameter was passed to this interpreter
       Shared parameters()
       ForEach parameters()
          If parameters() = parameter$
             ProcedureReturn 1
          EndIf
       Next
    EndProcedure
    Procedure.s getParameterValue(parameter$)
       ; returns the value of a specified parameter. so if there os a param like /number=51,
       ; you weill get 51, if you pass "/number" here (colons (:) and equals (=) are valid delimiters)
       Shared parameters()
       Protected length=Len(parameter$)
       ForEach parameters()
          If Left(parameters(), length) = parameter$
             Protected delimiter=FindString(parameters(), ":", 1)
             If Not delimiter
                delimiter=FindString(parameters(), "=", 1)
             EndIf
             If delimiter
                ProcedureReturn Mid(parameters(), delimiter+1)
             EndIf
          EndIf
       Next
    EndProcedure
    Procedure error(ErrorDescription$, stdout=0)
       ; outputs userdefined error to stderr and exits
       ; if 'stdout' is true or parameter -w was passed, then write it to stdout, too
       ; (including prepending http-header)
       ConsoleError(ErrorDescription$)
       If stdout Or isparameter("-w") Or isParameter("--warn")
          PrintN("Content-Type: text/html"+#LF$)
          PrintN(ErrorDescription$)
       EndIf
       End
    EndProcedure
    Procedure FindLastString(String$, StringToFind$, CaseInSensitive=0)
       Protected length=Len(StringToFind$), *position=@String$+(Len(String$)-length)<<#PB_Compiler_Unicode
       If StringToFind$
          While *position >= @String$
             If CompareMemoryString(*position, @StringToFind$, CaseInSensitive, length) = #PB_String_Equal
                ProcedureReturn (*position-@String$)>>#PB_Compiler_Unicode+1
             EndIf
             *position-SizeOf(Character)
          Wend
       EndIf
    EndProcedure

    ; apache sets an environment variable, named SCRIPT_FILENAME. this is the pb-file we
    ; have to compile. if this var is missing, our job is invalid and we can
    Define script_filename$=GetEnvironmentVariable("SCRIPT_FILENAME")
    If FileSize(script_filename$) = -1
       error("The environmentvariable SCRIPT_FILENAME contains an invalid or no filename: "+script_filename$)
    EndIf

    ; set neccessary environment-variables for the compiler, so you do not need 'export'.
    ; yes, this interpreter is self-configuring ;-)
    ; to get this working properly, place this interpreter in the root directory of purebasic (not in the
    ; 'compilers' directory!)
    SetEnvironmentVariable("PUREBASIC_HOME", GetPathPart(ProgramFilename()))
    ;SetEnvironmentVariable("PATH", GetEnvironmentVariable("PUREBASIC_HOME")+"compilers:"+GetEnvironmentVariable("PATH"))

    ; generate unique filename for the executable source code in this format:
    ; %TempDir%/pb-executbale_PathAndFilenameOfSourceWithoutSlash
    Define source$, exe$=GetTemporaryDirectory()+"pb-executable_"+RemoveString(script_filename$, "/")

    ; new compilation system: filenames are not random any more, but still unique (see line 88-90).
    ; we only have to compile again, if the source code has changed since the last compilation.
    ; if the executable already exists, we only have to run it. this saves time and disk space: one
    ; executable can be run by different users. the exe will remain in the temporary directory, however.
    ; to delete the exe nevertheless after usage, use the -f (f=force recompile) parameter. in this case the
    ; exe-filename is appended some random numbers to get a unique filename (to avoid conflicts when
    ; two users create the same exe with the same filename, whereas the user finishing firstly wants
    ; to delete the executable used by a second user)
    If isParameter("-f") Or isParameter("--force")
       exe$+FormatDate("_%yyyy-%mm-%dd_%hh-%ii-%ss_CanBeDeleted_", Date())+Str(Random(9999999))+".tmp"
       SetEnvironmentVariable("PB_Interpreter_ParamForce", "1")
    EndIf

    ; test, if (re-)compilation is neccessary or was enforced
    If GetFileDate(script_filename$, #PB_Date_Modified) > GetFileDate(exe$, #PB_Date_Created)
       ; generate a filename for the copy of the source code. it mus be unicq also, but will be deleted afterwards
       source$=exe$+".pb"
       
       ; in this section we copy the source file to leave the original source untouched. we edit the copy and send it
       ; to the compiler. first, we parse the file (if the specified parameters are set), then we comment the shebang-line.
       ; important: we need to filter the shebang-line, otherwise this would cause a syntax error.
       ; fastest method: copy it and comment the first line. (so the first line is always considered to be non-PB code!)
       ; if you know a faster method, please tell me (AND51 @ PB-forums)
       If isParameter("-p") Or isParameter("--parse") ; copying and parsing (if params are set)
          Define script=ReadFile(#PB_Any, script_filename$)
          If script
             Define tempfile=CreateFile(#PB_Any, source$)
             If tempfile
                Define parsing_mode$, n, line$, bom=ReadStringFormat(script)
                WriteStringFormat(tempfile, bom)
                WriteStringN(tempfile, ";"+ReadString(script, bom), bom)
                Define here_start=CreateRegularExpression(#PB_Any, "(?i)<<(|here|include|file|file|multi(|line)|(cr|)lf)$", #PB_RegularExpression_AnyNewLine|#PB_RegularExpression_MultiLine)
                Define here_end=CreateRegularExpression(#PB_Any, "^\s*<<", #PB_RegularExpression_AnyNewLine|#PB_RegularExpression_MultiLine)
                While Not Eof(script)
                   line$=ReadString(script, bom)
                   If MatchRegularExpression(here_start, line$)
                      n=FindLastString(line$, "<<")
                      WriteString(tempfile, Left(line$, n-1), bom) ; user should care about #DQUOTE$ by himself
                      parsing_mode$=LCase(Mid(line$, n+2))
                      Print(">>"+parsing_mode$)
                      Select parsing_mode$
                         Case "include", "file" ; include an external file and treat it as one string, i. e. replacing (cr)lf's
                            While Not Eof(script)
                               line$=ReadString(script, bom)
                               If MatchRegularExpression(here_end, line$)
                                  Break
                               Else
                                  Print(">>"+GetPathPart(script_filename$)+Trim(line$))
                                  Define include=ReadFile(#PB_Any, GetPathPart(script_filename$)+Trim(line$)), temp$
                                  Print(">>"+Str(include))
                                  If include
                                     Define include_bom=ReadStringFormat(include)
                                     While Not Eof(include)
                                        WriteString(tempfile, ReadString(include, include_bom), bom)
                                        If Not Eof(include)
                                           CompilerIf #PB_Compiler_OS = #PB_OS_Windows
                                              WriteString(tempfile, #DQUOTE$+"+#CRLF$+"+#DQUOTE$, bom)
                                           CompilerElse
                                              WriteString(tempfile, #DQUOTE$+"+#LF$+"+#DQUOTE$, bom)
                                           CompilerEndIf
                                        EndIf
                                     Wend
                                     CloseFile(include)
                                  Else
                                     error("Could not open '"+line$+"' as include file.")
                                  EndIf
                               EndIf
                            Wend
                         Case "crlf", "lf", "multiline", "multi", "here" ; normal HERE-document (like in perl) with crlf-replacement
                            While Not Eof(script)
                               line$=ReadString(script, bom)
                               If MatchRegularExpression(here_end, line$)
                                  Break
                               Else
                                  Select parsing_mode$
                                     Case "crlf"
                                        WriteString(tempfile, #DQUOTE$+"+#CRLF$+"+#DQUOTE$, bom)
                                     Case "lf"
                                        WriteString(tempfile, #DQUOTE$+"+#LF$+"+#DQUOTE$, bom)
                                     Default
                                        CompilerIf #PB_Compiler_OS = #PB_OS_Windows
                                           WriteString(tempfile, #DQUOTE$+"+#CRLF$+"+#DQUOTE$, bom)
                                        CompilerElse
                                           WriteString(tempfile, #DQUOTE$+"+#LF$+"+#DQUOTE$, bom)
                                        CompilerEndIf
                                  EndSelect
                               EndIf
                               WriteString(tempfile, line$, bom)
                            Wend
                         Default
                            While Not Eof(script)
                               line$=ReadString(script, bom)
                               If MatchRegularExpression(here_end, line$)
                                  Break
                               EndIf
                               WriteString(tempfile, line$, bom)
                            Wend
                      EndSelect
                      WriteStringN(tempfile, Mid(line$, FindString(line$, "<<", 1)+2), bom) ; #DQUOTE$ user should care for dquotes by himself
                   Else
                      WriteStringN(tempfile, line$, bom)
                   EndIf
                Wend
                CloseFile(script)
                CloseFile(tempfile)
             Else
                error("The source file cannot be created.")
             EndIf
          Else
             error("The source file cannot be read for parsing.")
          EndIf
       Else ; copying only (editing only first line)
          If CopyFile(script_filename$, source$)
             Define tempfile=OpenFile(#PB_Any, source$)
             If tempfile
                WriteString(tempfile, ";", ReadStringFormat(tempfile))
                CloseFile(tempfile)
             Else
                error("Temporary source file cannot be edited.")
             EndIf
          Else
             error("Source file cannot be copied.")
          EndIf
       EndIf
       
       ; timeout granted for compilation: 5 seconds (suggestion only!)
       Define compilation_time=ElapsedMilliseconds()
       
       ; compile the requested executable to the temp-folder, not to the same directory as the source code.
       ; --quiet disables all unneccessary text output; so there will only be text ouput, if a compiler error
       ; occured. so if AvailableProgramOutput() is zero, compilation was successful
       Define compiler=RunProgram(GetPathPart(ProgramFilename())+"compilers/pbcompiler", "--quiet --executable "+#DQUOTE$+exe$+#DQUOTE$+" "+#DQUOTE$+source$+#DQUOTE$, "", #PB_Program_Open|#PB_Program_Read)
       If Not compiler
          error("Compiler could not be started.")
       EndIf
       
       ; waiting if neccessary and catch the compilers output; if output > 0, then there is an error.
       ; we assume this, because of the --quiet flag (see line 125-128).
       ; kill the compiler and abort the whole procress, if timeout reached
       WaitProgram(compiler, 5000)
       Define compilerOutput=AvailableProgramOutput(compiler)
       
       If compilerOutput Or FileSize(exe$) = -1 Or ElapsedMilliseconds() > compilation_time+5000 ;Or ProgramRunning(compiler)
          Define compiler_error$, *compiler=AllocateMemory(compilerOutput)
          If *compiler
             ReadProgramData(compiler, *compiler, compilerOutput)
             compiler_error$=PeekS(*compiler)
          Else
             compiler_error$=ReadProgramString(compiler)+" (this was probably not the whole compiler output due to technical reasons)"
          EndIf
          CloseProgram(compiler)
          KillProgram(compiler)
          error("Compilation process did not finish correctly. Error: "+#DQUOTE$+compiler_error$+#DQUOTE$+".")
       EndIf
       
       ; new: compress the executable with UPX when you specify the parameter -c. as this step is optional, interpreter
       ; will continue in any case, even if the executable was not compressed (for whatever reason). furthermore, files
       ; seem to not be compressed, when they are too small anyway. we will use UPX's standard settings, as they
       ; offer a good balance between speed and compression ratio
       If isParameter("-c") Or isParameter("--compress")
          If Not RunProgram(GetPathPart(ProgramFilename())+"upx", "-qqq "+#DQUOTE$+exe$+#DQUOTE$, "", #PB_Program_Wait)
             ConsoleError("Something went wrong when compressing executable with UPX. Maybe the executable remains uncompressed. Resuming progress!")
          EndIf
       EndIf
    EndIf

    ; for convenience, we provide all QUERY_STRING-parameters as environment variables for the program:
    ; the URI "www.and51.de/index.pb?site=downloads&sort=asc" would create the environment variables
    ; "GET_site" with the value "downloads" and "GET_sort" with "asc". then you can easily catch them with the
    ; GetEnvironmentVariable() command. caution: you must decode them manually if neccessary with URLDecoder()
    If GetEnvironmentVariable("QUERY_STRING") And GetEnvironmentVariable("REQUEST_URI") ; check first param, but extract informations from second param
       Define query_exp=CreateRegularExpression(#PB_Any, "(?U)(?<=&).+(?==)")
       Dim querystrings.s(0)
       Define i, n=ExtractRegularExpression(query_exp, "&"+GetEnvironmentVariable("QUERY_STRING"), querystrings())
       For i=1 To n
          SetEnvironmentVariable("GET_"+querystrings(i-1), GetURLPart(GetEnvironmentVariable("REQUEST_URI"), querystrings(i-1)))
       Next
       SetEnvironmentVariable("PB_Interpreter_QueryStrings", Str(n))
    EndIf

    ; set some additional evironment variables to inform our later purebasic-executable about...
    SetEnvironmentVariable("PB_Interpreter_ProgramFilename", ProgramFilename())
    SetEnvironmentVariable("PB_Interpreter_Date", Str(Date()))
    SetEnvironmentVariable("PB_Interpreter_PBVersion", Str(#PB_Compiler_Version))
    If isParameter("-c") Or isParameter("--compress")
       SetEnvironmentVariable("PB_Interpreter_ParamCompress", "1")
    EndIf
    If isParameter("-w") Or isParameter("--warn")
       SetEnvironmentVariable("PB_Interpreter_ParamWarn", "1")
    EndIf
    If isParameter("-p") Or isParameter("--parse")
       SetEnvironmentVariable("PB_Interpreter_ParamParse", "1")
    EndIf
    SetEnvironmentVariable("PB_Interpreter_CreationTime", Str(ElapsedMilliseconds()-creation_time))

    ; now run our purebasic-executable. it will have all the environmentvariables that this process has.
    ; if we have POST-data, we will write it to the executable's input (stdin). therefore, the executable
    ; must open a console with OpenConsole() and read it with ReadConsoleData(). the executable
    ; can check, if there is POST-data: just call GetEnvironmentVariable("CONTENT_LENGTH"). this is
    ; the size of POST-data in bytes. if this env-var is non-existent or null, there is no data and the
    ; executable does not need to read anything; remember, a reading action with Input() or ReadConsoleData()
    ; blocks your program until an EOF has been written to the stdin. so avoid reading, if you can.
    ; we will also pass the ProgramParameter()'s, as described in line 19-30
    ; note 1: we will make use of WorkingDirectory$, so that the executable thinks, it would run in the same
    ; directory as the source code. so you get the feeling that your website only consists of .pb-files :-)
    ; Remember, the purebasic executable can read and set the directory with GetCurrentDirectory() and
    ; SetCurrentDirectory()
    ; note 2: the executable can also read its real directory with ProgramFilename()
    ; note 3: the executable is responsible for correct interaction between itself and the browser! therefore
    ; it needs to create a correct HTTP-header before generating any output. if you don't know what
    ; you must do, place these two lines at the top of your program:
    ;       PrintN("Content-Type: text/html")
    ;       PrintN("")
    ; note 4: all the executables output for the browser can be done with PrintN() and WriteConsoleData().
    ; note 5: remember all the env-vars, that we set for the executable. use them, if you need their informations
    ; note 6: the recommended way to create a "purebasic-made website" is this order:
    ;     1) OpenConsole()
    ;     2) read POST-Data, if neccessary
    ;     3) follow note 3
    ;     4) generate your output like in note 4
    ; note 7: try to avoid long breaks. my experience is, that when you have too long output-pauses,
    ; apache will simply dismiss your executable
    ; note 8: this interpreter now catches stderr, too. that means, you can write to stderr by using ConsoleError().
    ; text written to stderr should appear in the server's error_log!
    Define program=RunProgram(exe$, commandline$, GetPathPart(script_filename$), #PB_Program_Open|#PB_Program_Read|#PB_Program_Write|#PB_Program_Error)
    If program
       ; pass over POST-data, if existent
       Define content_length=Val(GetEnvironmentVariable("CONTENT_LENGTH"))
       If content_length
          Define *buffer=AllocateMemory(content_length)
          If *buffer
             ReadConsoleData(*buffer, content_length)
             WriteProgramData(program, *buffer, content_length)
          Else
             ConsoleError("Warning: POST-data cannot be passed to executable's stdin due to insufficient memory allocation.")
          EndIf
          WriteProgramData(program, #PB_Program_Eof, 0) ; not neccessary when using CloseProgram(), but then reading doesnt work either
       EndIf
       
       Define available, size, *output=AllocateMemory(1000), stderr$
       If Not *output
          CloseProgram(program)
          KillProgram(program)
          error("Cannot handle executable's output due to insufficient memory allocation.")
       EndIf
       While ProgramRunning(program)
          ; handle exe's stderr (stderr might appaer in the webserver's error_log!)
          stderr$=ReadProgramError(program)
          If stderr$
             ConsoleError(stderr$)
          EndIf
          
          ; handle exe's stdout
          available=AvailableProgramOutput(program)
          If available
             If available > 1000 ; ensure reading in steps of 1000 bytes only, so we don't need a large buffer
                available=1000
             EndIf
             size=ReadProgramData(program, *output, available)
             ; redirecting programs output directly to apache by writing it to OUR console (our stdout)
             WriteConsoleData(*output, size)
             Continue ; speeds up the data redirection by skipping the Delay() below, if there is a continuous data flow
          EndIf
          Delay(5) ; do not consume the whole cpu time. find the optimal value yourself and let me know, please
          
          ; circumvent a bug in ProgramRunning() (this command always returns 1),
          ; see http://www.purebasic.fr/english/viewtopic.php?t=37273
          If ProgramExitCode(program) <> -1 : Break : EndIf
       Wend
    Else
       error("The compiled program cannot be started: "+exe$)
    EndIf

    ; the program execution has finished. now we delete the exe$ and the temp source file if enforced
    ; (see line 91-98)
    If isParameter("-f") Or isParameter("--force")
       DeleteFile(exe$)
    EndIf
    If source$
       DeleteFile(source$)
    EndIf

    ; keep in mind that this interpreter can do any stuff here, e. g. append advertisement, increase any counters, etc.

    ; we do not need to tidy up the rest (free buffers and other things), because PB does this
    ; automatically on program exit. the console will also be closed automatically
    End
Thank you!

Olby.
Intel Core i7 Quad 2.3 Ghz, 8GB RAM, GeForce GT 630M 2GB, Windows 10 (x64)
User avatar
Vera
Addict
Addict
Posts: 858
Joined: Tue Aug 11, 2009 1:56 pm
Location: Essen (Germany)

Re: Linux Compilation Request

Post by Vera »

Hi Olby,

please see PM

greetings ~ Vera
Post Reply