Skip to main content
Simple Linux Compiler Tool         A little php script that compiles your application under linux systems

Hint: If you are looking for an easy to use build system for OGRE, have a look at the [[Building From Source]] page.

Introduction

This is a little php script that compiles your application under linux systems. I'm not that sure if it works, because I wrote this long time ago and I'm not able to test it at the moment, but I was asked to wiki it. So here it is:

You need to copy the two php files you find on the bottom of this entry to some directory, e.g. the root of your project. For example we take a little project from me: a library with some common classes like an angle-class and so on. The project is called libTools and it creates the file libTools.so. The following is the directory structure of the project:

Copy to clipboard
/game - the root of all my projects /game/Tools - the root of the project libTools /game/Tools/src - the directory which contains the source code /game/Tools/include - the directory which contains the headers /game/Tools/Build - the directory I want the temporary files to be saved


I copied the both php files to /game. In this directory the *.PROJECT files have to be stored (they are similar to the .dsp files of MSVC, or the .DEV files from Dev-C++). This is the .PROJECT file of the example project, it's called libTools.PROJECT, but it maybe called however you want. Only the extension .PROJECT is important.

The *.PROJECT files

Tools.PROJECT

Copy to clipboard
<? //the name of your project - this name is the target you have to pass to the build tool later $NAME = "libTools"; $Project[$NAME] = Array( //WorkingDir: the dir in which the objects will be stored "WorkingDir" => "Build", //ShortDesc: a short description. This is showed by "build.php info [target]" "ShortDesc" => "Network and Logging tools", //Description: a more detailed description. This is showed by "build.php info [target]" "Description" => "%(c) 2005 by CORVUS MEDIA", "Output" => Array( //Filename: the filename of the executable/library you want to create "Filename" => "libTools.so", //Path: the path where this file shall be stored "Path" => "/usr/lib", //SU: set this to true if you want the build tool to change to superuser mode before // copying the file to it's destination (e.g. if you want to copy it to /usr/lib) "SU" => true, ), "Compiler" => Array( //Includes: a list of directories that contain headers "Includes" => Array( "Tools/include", ), //Packages: a list of packages the compiler needs to use (for pkg-config) "Packages" => Array( "OGRE", ), //Flags: additional compiler flags if needed "Flags" => "-g -O2", ), "Linker" => Array( //Libraries: a list of libraries that you want to be linked to "Libraries" => Array( ), //LibPaths: a list of directories which contain libraries the linker links to "LibPaths" => Array( ), //Packages: see Compiler: Packages "Packages" => Array( "OGRE", ), //Flags: see Compiler: Flags "Flags" => "-g -O2 -shared ", ), //SourceDirs: a list of directories in which the compiler compiles all *.cpp files "SourceDirs" => Array( "Tools/src", ), //AdditionalSourceFiles: a list of single source files you want the compiler to compile, which // are not in the SourceDirs (see above) "AdditionalSourceFiles" => Array( ), ); ?>

How to use

To use the build tool: Change to the directory that contains the both php files and execute build.php. You will be guided through the commands.
Here some of the common commands for the example:

Copy to clipboard
~# cd /game /game# php build.php info libTools /game# php build.php config libTools /game# php build.php build libTools /game# php build.php clean libTools


Explanation:
info will show you some information about the target (in this case libTools): mainly it shows the descriptions<br />
config will check if everything is ready and gather information from packages etc.<br />
build will compile all ConfigDirs and AdditionConfigFiles and link them. Then it will copy them to Output/Path<br />
clean will delete all temporary build files, for example objects

Requirements

You need PHP, pkg-config, a compiler (e.g. gcc) and a linker (e.g. g++). This should be all.

Information

The build tool will automatically check for every source file if the headers which are included by the source file have changed since the last build (this is done by comparing the ctime of the header and the object). If the objects got deleted it recompiles the source files.
The tool actually should work. Maybe some paths to the compiler or so are wrong for your distribution. It's written under SuSE Linux Professional 9.2.

Tip_icon.png Attention! This tool was actually only coded for home use. It may be specific for my projects!
If you have problems you may post in this thread in the help forum:
http://www.ogre3d.org/phpBB2/viewtopic.php?t=12786

The php files

Attention! The line breaks in the following to files are no mistakes!
build.php

Copy to clipboard
<? include "functions.php"; function gettargets() { $targets = rscan(".", ".PROJECT"); if (count($targets) == 0) $targets_txt = " no targets found!\n"; else { $targets_txt = ""; foreach ($targets as $t) { include($t); $pos = strrpos($t, "/"); if ($pos !== false) $pos++; $part1 = " $NAME"; if (strlen($part1) > 27) $part1 = substr($part1, 0, 27); else $part1 .= str_repeat(" ", 27-strlen($part1)); $targets_txt .= "$part1 -- ".substr($Project[$NAME]["ShortDesc"], 0, 40)."\n"; } } return $targets_txt; } echo "############### PALABA BUILD SYSTEM ###############\n"; register_parameters(); $rebuild = false; $info = false; $conf = false; $clean = false; if ($argc == 1) { $targets_txt = gettargets(); $t = explode("\n", $targets_txt); $c = 1; $tnames[0] = "all"; $rnames[0] = "all"; foreach ($t as $i => $v) { if (trim($v) != "") { $t[$i] = (($c < 10) ? " " : " ") . $c . " |". substr($v, 1); $tnames[$c] = "'".trim(substr(trim($v), 0, strpos(trim($v), " ")))."'"; $rnames[$c] = trim(substr(trim($v), 0, strpos(trim($v), " "))); $c++; } } $targets_txt = implode("\n", $t); echo "No parameters set, maybe you should take a look at 'build help' Aviable targets: id | target 0 | all $targets_txt If you want to build any targets, you can now enumerate them. Please type in the ids of the targets you want to build seperated by spaces. Example: 1 2 ids to build: "; $tt_build = trim(fgets(STDIN)); echo " Do you want a to rebuild those targets (y/n)? "; $reb = trim(fgets(STDIN)); if ($reb == "y") $rebuild = true; $a = explode(" ", "$tt_build "); $b = Array(); $s = Array(); foreach ($a as $i => $v) { if ($v == "0") { $b = Array("0"); break; } else if ($v == "") { } else { if (intval($v) >= $c) $a[$i] = -1; else { if (!$s[intval($v)]) { $s[intval($v)] = true; $b[] = intval($v); } } } } $max = count($b); if ($max == 1) { $text = $tnames[$b[0]]; $argv[] = $rnames[$b[0]]; } elseif ($max == 0) die(); else { $text = ""; foreach ($b as $i => $v) { if ($v > 0) { $concat = ($i == $max - 1) ? " and " : ", "; if ($i > 0) $text .= $concat; $text .= $tnames[$v]; $argv[] = $rnames[$v]; } } } echo " Do you really want to ".(($rebuild)?"re":"")."build $text? (y/n) "; $sure = trim(fgets(STDIN)); if ($sure != "y") die(); } if ($params["help"]) { $targets_txt = gettargets(); echo "Possible commands: build all -- build all targets build [target] -- build special target build rebuild all -- rebuild all targets build rebuild [target] -- rebuild special target build clean all -- remove temporary files build clean [target] -- remove temporary files build config all -- configure the targets build config [target] -- configure the targets build help -- show this screen build info [target] -- show information Possible targets: $targets_txt "; die(); } else if ($params["rebuild"]) { $rebuild = true; $argv[1] = $argv[0]; array_shift($argv); } else if ($params["clean"]) { $clean = true; $argv[1] = $argv[0]; array_shift($argv); } else if ($params["info"]) { $info = true; $argv[1] = $argv[0]; array_shift($argv); } else if ($params["config"]) { $conf = true; $argv[1] = $argv[0]; array_shift($argv); } $targets = $argv; array_shift($targets); if (count($targets) == 0) { echo "ERROR: no targets\n"; die(); } $configs = rscan(".", ".CONFIG"); foreach ($configs as $cfg) include($cfg); $avaiable_targets = rscan(".", ".PROJECT"); $av_tar_name = Array(); foreach ($avaiable_targets as $target) { include($target); $av_tar_name[] = $NAME; } if ($argv[1] == "all") $targets = $av_tar_name; echo "Targets in queue: ".implode(" ", $targets)."\n"; $missing_targets = Array(); foreach ($targets as $target) if (!is_array($Project[$target])) $missing_targets[] = $target; if (count($missing_targets) > 0) { echo "\nERROR: Missing targets: ".implode(" ", $missing_targets)."\n\n"; die(); } echo "\nAll targets found, continuing!\n"; foreach ($targets as $target) { if ($info) printDesc($target); else if ($clean) clean($target); else if ($conf) configure($target); else build($target, $rebuild); } ?>


functions.php

Copy to clipboard
<? function register_parameters() { global $params, $argv; $params = Array(); $params[$argv[1]] = true; } function rscan($dir, $file) { $ret = Array(); $w = dir($dir); while ($f = $w->read()) { if ($f != "." && $f != "..") { if (is_file($dir."/".$f)) { if (substr($f, -strlen($file)) == $file) $ret[] = $dir."/".$f; } else $ret = array_merge($ret, rscan($dir."/".$f, $file)); } } $w->close(); return $ret; } function printDesc($target) { global $Project; $p = $Project[$target]; $tmp = explode("\n", $p["Description"]."\n"); $desc = ""; foreach ($tmp as $e) { $centered = false; if (substr($e, 0, 1) == "%") { $centered = true; $e = substr($e, 1); } $e = substr($e, 0, 42); if ($centered) { //centered $rest = 42 - strlen($e); $ladd = floor($rest/2); $radd = ceil ($rest/2); } else { $ladd = 0; $radd = 42 - strlen($e); } $desc .= "# ".str_repeat(" ", $ladd)."$e".str_repeat(" ", $radd)." #\n"; } if (strlen($target) <= 42) { $rest = 42 - strlen($target); $ladd = floor($rest/2); $radd = ceil ($rest/2); $target_txt = str_repeat(" ", $ladd).$target.str_repeat(" ", $radd); } else $target_txt = substr($target, 0, 42); echo " ############################################## # $target_txt # ############################################## # # $desc############################################## "; } function build($target, $rebuild = false) { global $Project, $Config, $objects, $error, $counter, $cmd; $objects = Array(); $p = $Project[$target]; $c = $Config[$target]; if ($c["COMPILER"] == "") $c["COMPILER"] = "g++"; if ($c["LINKER"] == "") $c["LINKER"] = "g++"; if ($c["PKGCONFIG"] == "") $c["PKGCONFIG"] = "pkg-config"; printDesc($target); $wd = $p["WorkingDir"]; if (!is_dir($wd)) mkdir($wd); $cmd = $c["COMPILER"]." -c "; $cmd .= $p["Compiler"]["Flags"]; $cmd .= $c["CFLAGS"]; if (count($p["Compiler"]["Packages"]) && $c["PKGCONFIG"] != "-") { $pkgflags = ""; exec($c["PKGCONFIG"]." --cflags ".implode(" ", $p["Compiler"]["Packages"])." 2>&1", $pkgflags); $cmd .= " $pkgflags[0] "; } foreach ($p["Compiler"]["Includes"] as $inc) $cmd .= " -I$inc"; foreach ($p["SourceDirs"] as $sd) { $error = false; $wdn = $wd."/".$sd; mkdir2($wdn); echo "Diving into SourceDir '$sd':\n"; $d = @dir($sd); if ($d === false) echo "Warning: Could not find SourceDir '$sd'! Resuming.\n"; else { if ($rebuild) { echo "Removing objects (rebuild mode)\n"; while ($file = $d->read()) { if ($file != "." && $file != ".." && is_file($sd."/".$file)) { $file = "$wdn/".substr($file, 0, strrpos($file, ".")).".o"; if (file_exists($file)) unlink($file); } } $d->rewind(); } $counter = 0; while ($file = $d->read()) { if ($file != "." && $file != ".." && is_file($sd."/".$file) && (substr($file, -4) == ".cpp" || substr($file, -2) == ".c" || substr($file, -3) == ".cc")) compileFile($file, $wdn, $sd); } echo "$counter files compiled".($error?" - errrors occured!\n\n" : " - everything OK\n\n"); } } $asf = $p["AdditionSourceFiles"]; if (count($asf)) { $error = false; echo "\nProcessing additional files:\n"; foreach ($asf as $as) { if (strpos($as, "/") === false) { $path = "."; $file = $as; } else { $path = substr($as, 0, strrpos($as, "/")); $file = substr($as, strlen($path) + 1); } if (!is_file("$path/$file")) { echo "ERROR: Cannot find additional source file '$path/$file' Continue? (y/n) "; $yn = trim(fgets(STDIN)); if ($yn != "y") die(); } else { $wdn = "$wd/$path"; mkdir2($wdn); compileFile($file, $wdn, $path); } } echo "$counter files compiled".(($error)?" - errrors occured!\n\n" : " - everything OK\n\n"); } if ($error) return; if ($p["Output"]["SU"]) echo "Maybe you will be asked for your root password now:\n"; $exec_cmd = $p["Linker"]["Flags"] . $c["LFLAGS"]; if (count($p["Linker"]["Packages"]) && $c["PKGCONFIG"] != "-") { $pkgflags = ""; exec($c["PKGCONFIG"]." --libs ".implode(" ", $p["Linker"]["Packages"])." 2>&1", $pkgflags); $exec_cmd .= " $pkgflags[0] "; } foreach ($p["Linker"]["Libraries"] as $lib) $exec_cmd .= " -l$lib"; foreach ($p["Linker"]["LibPaths"] as $lp) $exec_cmd .= " -L$lp"; $exec_cmd = (($p["Output"]["SU"]) ? "sudo " : "").$c["LINKER"]." -pass-exit-codes $exec_cmd -o ".$p["Output"]["Path"]."/".$p["Output"]["Filename"]." ".implode(" ", $objects)." 2>&1"; if ($counter > 0 || !file_exists($p["Output"]["Path"]."/".$p["Output"]["Filename"]) || timecheck2($p["Output"]["Path"]."/".$p["Output"]["Filename"], $objects)) { echo " [LD] ".$p["Output"]["Path"]."/".$p["Output"]["Filename"]."\n"; exec($exec_cmd, $output, $ret); $error = false; if ($ret > 0) { echo "ERROR while linking! Do you want to see the linker output? (y/n) "; $line = trim(fgets(STDIN)); if ($line == "y") { echo "Linker output: $exec_cmd\n"; echo implode("\n", $output); } echo " Do you want to save the linker output to link.log? (y/n) "; $line = trim(fgets(STDIN)); if ($line == "y") { $fh = fopen("link.log", "w+"); fwrite($fh, "Linker output: $exec_cmd ".implode("\n", $output)); fclose($fh); } echo " Continue? (y/n) "; $yn = trim(fgets(STDIN)); if ($yn != "y") { echo"\n"; die(); } $error = true; } echo "File successfully linked - ".(($error)?"errors occured!\n":"everything OK\n"); } if (!$error) echo "Build of '$target' completed!\n"; echo "\n\n"; } function compileFile($file, $wdn, $sd) { global $cmd, $objects, $counter, $error; $output = ""; $ret = ""; $filename = substr($file, 0, strrpos($file, ".")); $object = "$wdn/$filename.o"; $objects[] = $object; $exec_cmd = "$cmd -o $object $sd/$file 2>&1"; if (timecheck($cmd, "$sd/$file", $object)) { if (is_file($object)) unlink($object); $counter++; echo " [CC] $file\n"; exec($exec_cmd, $output, $ret); if ($ret > 0) { echo "ERROR while compiling!\nDo you want to see the compilers output? (y/n) "; $line = trim(fgets(STDIN)); if ($line == "y") { echo "Compiler output: $exec_cmd\n"; echo implode("\n", $output); } echo " Do you want to continue? (y/n) "; $error = true; $line = trim(fgets(STDIN)); if ($line != "y") { echo "\n\n"; die(); } } } } function timecheck($cmd, $file, $obj, $level = 1) { if ($level >= 4) return false; if (@filemtime($obj) < filemtime($file)) return true; $content = file_get_contents($file); preg_match_all("/\#include [\<\"]([^\n]*)[\>\"]/si", $content, $out); preg_match_all("/\-I([^ ]*)/si", $cmd, $out2); $headers = $out[1]; $paths = $out2[1]; foreach ($headers as $header) { if (is_file($header)) // if (filemtime($header) > filemtime($obj)) if (timecheck($cmd, $header, $obj, $level + 1)) return true; foreach ($paths as $path) { if (is_file($path."/".$header)) // if (filemtime("$path/$header") > filemtime($obj)) if (timecheck($cmd, "$path/$header", $obj, $level + 1)) return true; } } return false; } function timecheck2($target, $objs) { $tt = filemtime($target); foreach ($objs as $obj) { if (!file_exists($obj)) return true; else if (filemtime($obj) > $tt) return true; } return false; } function mkdir2($dir) { if (is_dir($dir)) return; $dir_above = substr($dir, 0, strrpos($dir, "/")); mkdir2($dir_above); mkdir($dir); } function clean($target) { global $Project; $objects = Array(); $p = $Project[$target]; $wd = $p["WorkingDir"]; foreach ($p["SourceDirs"] as $sd) { $wdn = $wd."/".$sd; echo "Diving into SourceDir '$sd':\n"; if (!is_dir($wdn)) echo "Warning: Could not find TempObjDir '$wdn'! Resuming.\n"; $d = @dir($sd); if ($d === false) echo "Warning: Could not find SourceDir '$sd'! Resuming.\n"; else { echo "Removing objects\n"; while ($file = $d->read()) { if ($file != "." && $file != ".." && is_file($sd."/".$file)) { $file = "$wdn/".substr($file, 0, strrpos($file, ".")).".o"; if (file_exists($file)) unlink($file); } } } } echo "Clean complete!\n\n"; } function configure($target) { global $Project, $Config; $p = $Project[$target]; $c = Array(); echo "Configuring $target:\n"; ////// run some tests // check for programs: g++ pkg-config $ncmd = checkForCMD("g++", "g++", false); $c["COMPILER"] = $ncmd; $c["LINKER"] = $ncmd; if (count($p["Linker"]["Packages"]) + count($p["Compiler"]["Packages"]) > 0) { if (($ncmd = checkForCMD("pkg-config", "pkg-config")) === false) { $cflags = ""; $lflags = ""; foreach ($p["Compiler"]["Packages"] as $pck) { echo "Specify the compiler flags (CFLAGS) needed for $pck : "; $cflags .= " " . trim(fgets(STDIN)); } foreach ($p["Linker"]["Packages"] as $pck) { echo "Specify the linker flags (LFLAGS) needed for $pck : "; $lflags .= " " . trim(fgets(STDIN)); } $c["CFLAGS"] = $cflags; $c["LFLAGS"] = $lflags; $c["PKGCONFIG"] = "-"; } else $c["PKGCONFIG"] = $ncmd; } else $c["PKGCONFIG"] = "-"; //test for modules: if ($c["PKGCONFIG"] != "-") { $ps = array_unique(array_merge($p["Compiler"]["Packages"], $p["Linker"]["Packages"])); foreach ($ps as $pkg) { $nc = checkForPKG($c["PKGCONFIG"], $pkg); $c["CFLAGS"] .= " ".$nc["CFLAGS"]; $c["LFLAGS"] .= " ".$nc["LFLAGS"]; } } //test for pathes: foreach ($p["Compiler"]["Includes"] as $dir) if (!is_dir($dir)) echo "Warning: '$dir' not found (Compiler>Includes)\n"; preg_match_all("/\-I([^ ]*) /", $p["Compiler"]["Flags"]." ".$c["CFLAGS"], $ret); foreach ($ret[1] as $dir) if (!is_dir($dir)) echo "Warning: '$dir' not found (Compiler>Flags)\n"; foreach ($p["Linker"]["LibPaths"] as $dir) if (!is_dir($dir)) echo "Warning: '$dir' not found (Linker>LibPaths)\n"; preg_match_all("/\-I([^ ]*) /", $p["Linker"]["Flags"]." ".$c["LFLAGS"], $ret); foreach ($ret[1] as $dir) if (!is_dir($dir)) echo "Warning: '$dir' not found (Linker>Flags)\n"; foreach ($p["SourceDirs"] as $dir) if (!is_dir($dir)) echo "Warning: '$dir' not found (SourceDirs)\n"; foreach ($p["AdditionalSourceFiles"] as $file) if (!is_file($file)) echo "Warning: '$file' not found (AdditionalSourceFiles)\n"; //test for libraries $linkpathes = ""; foreach ($p["Linker"]["LibPaths"] as $dir) $linkpaths .= " -L$dir"; preg_match_all("/\-I([^ ]*) /", $p["Linker"]["Flags"]." ".$c["LFLAGS"], $ret); foreach ($ret[1] as $dir) $linkpaths .= " -L$dir"; foreach ($p["Linker"]["Libraries"] as $lib) { echo "test for lib '$lib'..."; exec("ld -o /dev/null --noinhibit-exec --unresolved-symbols=ignore-all $linkpaths -l$lib 2>&1", $out, $ret); if ($ret != 0) echo "failed\nWarning: '$lib' not found (Linker>Libraries)\n"; else echo "ok\n"; } preg_match_all("/\-l([^ ]*) /", $p["Linker"]["Flags"]." ".$c["LFLAGS"], $ret); foreach ($ret[1] as $lib) { echo "test for lib '$lib'..."; exec("ld --noinhibit-exec --unresolved-symbols=ignore-all $linkpaths -l$lib 2>&1", $out, $ret); if ($ret != 0) echo "failed\nWarning: '$lib' not found (Linker>Libraries)\n"; else echo "ok\n"; } echo "Configuration completed!\n\n"; $fh = fopen("$target.CONFIG", "w+"); $config_code = var_export($c, true); fwrite($fh, "<? \$Config[\"$target\"] = $config_code ?> "); fclose($fh); $Config[$target] = $c; } function checkForPKG($cmd, $pkg) { echo "test for pkg $pkg..."; exec("$cmd --exists $pkg 2>&1", $out, $ret); if ($ret == 0) { echo "ok\n"; return Array(); } echo "failed Do you want to set your own flags? (y/n) "; $yn = trim(fgets(STDIN)); $name = Array( "OGRE" => "Ogre3D Rendering Engine", "CEGUI" => "Crazy Eddies Gui System", ); if ($yn != "y") die("ERROR: Could not find package $pkg ".(($name[$pkg])?"Are you sure you have installed ".$name[$pkg]."?":"")." " ); echo "Specify the compiler flags (CFLAGS) needed for $pkg : "; $cflags .= " " . trim(fgets(STDIN)); echo "Specify the linker flags (LFLAGS) needed for $pkg : "; $lflags .= " " . trim(fgets(STDIN)); $c = Array(); $c["CFLAGS"] = $cflags; $c["LFLAGS"] = $lflags; return $c; } function checkForCMD($org, $cmd, $bSel = true) { echo "test for $org..."; exec("$cmd --version 2>&1", $out, $ret); if ($ret > 0) { echo "failed\nERROR: Cannot find $cmd "; if ($bSel) { echo "Do you want to use $org? (y/n) "; $yn = trim(fgets(STDIN)); } else $yn = "y"; if ($yn == "y") { echo "Please specify the full command to $org (i.e. /usr/bin/$org) : "; $new_cmd = trim(fgets(STDIN)); return checkForCMD($org, $new_cmd, $bSel); } else { return false; } } else { echo "ok\n"; return $cmd; } } ?>