Project.php 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615
  1. <?php
  2. //-----------------------------------------------------------------------------
  3. // Copyright (c) 2012 GarageGames, LLC
  4. //
  5. // Permission is hereby granted, free of charge, to any person obtaining a copy
  6. // of this software and associated documentation files (the "Software"), to
  7. // deal in the Software without restriction, including without limitation the
  8. // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
  9. // sell copies of the Software, and to permit persons to whom the Software is
  10. // furnished to do so, subject to the following conditions:
  11. //
  12. // The above copyright notice and this permission notice shall be included in
  13. // all copies or substantial portions of the Software.
  14. //
  15. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  16. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  17. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  18. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  19. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  20. // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
  21. // IN THE SOFTWARE.
  22. //-----------------------------------------------------------------------------
  23. ///
  24. /// Project info
  25. ///
  26. class Project
  27. {
  28. public static $TYPE_APP = 'app';
  29. public static $TYPE_SHARED_APP = 'sharedapp';
  30. public static $TYPE_LIB = 'lib';
  31. public static $TYPE_SHARED_LIB = 'shared';
  32. public static $TYPE_ACTIVEX = 'activex';
  33. public static $TYPE_SAFARI = 'safari';
  34. public static $TYPE_CSPROJECT = 'csproj';
  35. public $name; // Project name
  36. public $guid; // Project GUID
  37. public $type; // Application or Library?
  38. public $dir_list; // What directories are we checking in?
  39. public $outputs; // List of outputs we want to generate.
  40. public $game_dir; // Base product path
  41. public $defines; // Preprocessor directives
  42. public $disabledWarnings; // Additional warnings to disable
  43. public $includes; // Additional include paths
  44. public $libs; // Additional libraries to link against
  45. public $lib_dirs; // Additional library search paths
  46. public $lib_includes; // libs to include (generated by modules)
  47. public $additionalExePath; // Additional section to inject into executable path
  48. public $dependencies; // Projects this project depends on
  49. public $references; // for managed projects, references to required assemblies
  50. public $moduleDefinitionFile; // definition file to control shared library exports on windows
  51. public $projectFileExt;
  52. public $commandDebug = "";
  53. public $commandOptimized = "";
  54. public $commandRelease = "";
  55. public $argsDebug = "";
  56. public $argsOptimized = "";
  57. public $argsRelease = "";
  58. public $projSubSystem = 2; // support for Windows/Console/Assembly linker subsystem (1 - Console, 2 - Windows, 3 - Assembly)
  59. private static $xUID = 1; // used for unique file IDs for Xcode projects
  60. public $uniformOutputFile = 0; // debug/release builds use same filename (necessary for np plugin)
  61. // $additionalExePath, $lib_dirs, $libs, all appear to be unused. [pauls 11/9/2007]
  62. public function Project( $name, $type, $guid = '', $game_dir = 'game', $output_name = '' )
  63. {
  64. if (strlen($output_name) == 0)
  65. $output_name = $name;
  66. $this->name = $name;
  67. $this->outputName = $output_name;
  68. $this->guid = $guid;
  69. $this->type = $type;
  70. $this->game_dir = $game_dir;
  71. $this->dir_list = array();
  72. $this->defines = array();
  73. $this->includes = array();
  74. $this->libs = array();
  75. $this->lib_dirs = array();
  76. $this->lib_includes = array();
  77. $this->outputs = array();
  78. $this->dependencies = array();
  79. $this->disabledWarnings = array();
  80. $this->references = array();
  81. }
  82. public function isApp()
  83. {
  84. return $this->type == self::$TYPE_APP;
  85. }
  86. public function isSharedApp()
  87. {
  88. return $this->type == self::$TYPE_SHARED_APP;
  89. }
  90. public function isLib()
  91. {
  92. return $this->type == self::$TYPE_LIB;
  93. }
  94. public function isSharedLib()
  95. {
  96. return $this->type == self::$TYPE_SHARED_LIB;
  97. }
  98. public function isCSProject()
  99. {
  100. return $this->type == self::$TYPE_CSPROJECT;
  101. }
  102. public function isActiveX()
  103. {
  104. return $this->type == self::$TYPE_ACTIVEX;
  105. }
  106. public function isSafari()
  107. {
  108. return $this->type == self::$TYPE_SAFARI;
  109. }
  110. public function setUniformOutputFile()
  111. {
  112. return $this->uniformOutputFile = 1;
  113. }
  114. public function setSubSystem( $subSystem )
  115. {
  116. $this->projSubSystem = $subSystem;
  117. }
  118. public function validate()
  119. {
  120. // Sort the path list
  121. sort( $this->dir_list );
  122. // Make sure we don't have any duplicate paths
  123. $this->dir_list = array_unique( $this->dir_list );
  124. }
  125. public function addReference($refName, $version = "")
  126. {
  127. $this->references[$refName] = $version;
  128. }
  129. public function addIncludes( $includes )
  130. {
  131. $this->includes = array_merge( $includes, $this->includes );
  132. }
  133. public function validateDependencies()
  134. {
  135. $pguids = array();
  136. foreach( $this->dependencies as $pname )
  137. {
  138. $p = Generator::lookupProjectByName( $pname );
  139. if( $p )
  140. array_push( $pguids, $p->guid );
  141. else
  142. trigger_error( "Project dependency not found: " .$pname, E_USER_ERROR );
  143. }
  144. // todo: change to dependencyGuids
  145. $this->dependencies = $pguids;
  146. }
  147. private function generateXUID()
  148. {
  149. return sprintf( "%023X", Project::$xUID++ );
  150. }
  151. private function createFileEntry( $output, $curPath, $curFile )
  152. {
  153. // See if we need to reject it based on our rules..
  154. if( $output->ruleReject( $curFile ) )
  155. return null;
  156. // Get the extension - is it one of our allowed values?
  157. if( !$output->allowedFileExt( $curFile ) )
  158. return null;
  159. // Cool - note in the list!
  160. $newEntry = new stdClass();
  161. $newEntry->name = $curFile;
  162. $newEntry->path = FileUtil::collapsePath( $curPath . "/" . $curFile );
  163. if ( !FileUtil::isAbsolutePath( $newEntry->path ) )
  164. {
  165. // This could be consolidated into a single OR statement but it is easier to
  166. // read as two separate if's
  167. if ( !Generator::$absPath )
  168. $newEntry->path = $output->project_rel_path . $newEntry->path;
  169. if ( Generator::$absPath && !stristr($newEntry->path, Generator::$absPath) )
  170. $newEntry->path = $output->project_rel_path . $newEntry->path;
  171. }
  172. // Store a project-unique ID here for Xcode projects
  173. // It will be appended by a single char in the templates.
  174. $newEntry->hash = Project::generateXUID();
  175. return $newEntry;
  176. }
  177. function generateFileList( &$projectFiles, $outputName, &$output )
  178. {
  179. $projName = $this->name;
  180. $projectFiles[ $projName ] = array();
  181. foreach( $this->dir_list as $dir )
  182. {
  183. $dir = FileUtil::normalizeSlashes( $dir );
  184. // Build the path.
  185. if ( FileUtil::isAbsolutePath( $dir ) )
  186. $curPath = $dir;
  187. else
  188. $curPath = FileUtil::collapsePath( $output->base_dir . $dir );
  189. $pathWalk = &$projectFiles[ $projName ];
  190. if ( Generator::$absPath )
  191. {
  192. if ( stristr($curPath, getEngineSrcDir()) || stristr($curPath, getLibSrcDir()) )
  193. $curPath = Generator::$absPath . "/". str_replace("../", "", $curPath);
  194. }
  195. // Check if its a file or a directory.
  196. // If its a file just add it directly and build a containng filter/folder structure,
  197. // for it else if a dir add all files in it.
  198. if( is_file( $curPath ) )
  199. {
  200. // Get the file name
  201. $curFile = basename( $curPath );
  202. $curPath = dirname( $curPath );
  203. //echo( "FILE: " . $curFile . " PATH: " . $curPath . "\n" );
  204. }
  205. if( is_dir( $curPath ) )
  206. {
  207. //echo( "DIR: " . $curPath . "\n" );
  208. // Get the array we'll be adding things to...
  209. $pathParts = explode( '/', FileUtil::collapsePath( $dir ) );
  210. foreach( $pathParts as $part )
  211. {
  212. // Skip parts that are relative paths - only want meaningful directories.
  213. if( $part == '..' )
  214. continue;
  215. if( !is_array( $pathWalk[ $part ] ) )
  216. $pathWalk[ $part ] = array();
  217. $pathWalk = &$pathWalk[ $part ];
  218. }
  219. // Open directory.
  220. //echo( "SCANNING: " . $curPath . "\n");
  221. $dirHdl = opendir( $curPath );
  222. if( !$dirHdl )
  223. {
  224. echo( "Path " . $curPath . " not found, giving up.\n" );
  225. return false;
  226. }
  227. // Iterate over all the files in the path if not a single file spec.
  228. if( !$curFile )
  229. {
  230. while( $curFile = readdir( $dirHdl ) )
  231. {
  232. // Skip out if it's an uninteresting dir...
  233. if( $curFile == '.' || $curFile == '..' || $curFile == '.svn' || $curFile == 'CVS' )
  234. continue;
  235. $newEntry = $this->createFileEntry( $output, $curPath, $curFile );
  236. if( $newEntry )
  237. $pathWalk[] = $newEntry;
  238. }
  239. }
  240. else
  241. {
  242. $newEntry = $this->createFileEntry( $output, $curPath, $curFile );
  243. if( $newEntry )
  244. $pathWalk = $newEntry;
  245. $curFile = '';
  246. }
  247. // Clean up after ourselves!
  248. closedir( $dirHdl );
  249. }
  250. }
  251. FileUtil::trimFileList( $projectFiles );
  252. // Uncomment me to see the structure the file lister is returning.
  253. //print_r($projectFiles);
  254. return true;
  255. }
  256. private function setTemplateParams( $tpl, $output, &$projectFiles )
  257. {
  258. // Set the template delimiters
  259. $tpl->left_delimiter = $output->ldelim ? $output->ldelim : '{';
  260. $tpl->right_delimiter = $output->rdelim ? $output->rdelim : '}';
  261. $gameProjectName = getGameProjectName();
  262. // Evaluate template into a file.
  263. $tpl->assign_by_ref( 'projSettings', $this );
  264. $tpl->assign_by_ref( 'projOutput', $output );
  265. $tpl->assign_by_ref( 'fileArray', $projectFiles );
  266. $tpl->assign_by_ref( 'projName', $this->name );
  267. $tpl->assign_by_ref( 'projOutName', $this->outputName );
  268. $tpl->assign_by_ref( 'gameFolder', $this->game_dir );
  269. $tpl->assign_by_ref( 'GUID', $this->guid );
  270. $tpl->assign_by_ref( 'projDefines', $this->defines );
  271. $tpl->assign_by_ref( 'projDisabledWarnings', $this->disabledWarnings );
  272. $tpl->assign_by_ref( 'projIncludes', $this->includes );
  273. $tpl->assign_by_ref( 'projLibs', $this->libs );
  274. $tpl->assign_by_ref( 'projLibDirs', $this->lib_dirs );
  275. $tpl->assign_by_ref( 'projDepend', $this->dependencies );
  276. $tpl->assign_by_ref( 'gameProjectName', $gameProjectName );
  277. $tpl->assign_by_ref( 'projModuleDefinitionFile', $this->moduleDefinitionFile );
  278. $tpl->assign_by_ref( 'projSubSystem', $this->projSubSystem );
  279. if (Generator::$useDLLRuntime)
  280. {
  281. // /MD and /MDd
  282. $tpl->assign( 'projRuntimeRelease', 2 );
  283. $tpl->assign( 'projRuntimeDebug', 3 );
  284. }
  285. else
  286. {
  287. // /MT and /MTd
  288. $tpl->assign( 'projRuntimeRelease', 0 );
  289. $tpl->assign( 'projRuntimeDebug', 1 );
  290. }
  291. if (!$this->commandDebug && ( $this->isSharedLib() || $this->isSharedApp() ))
  292. {
  293. $command = "$(TargetDir)\\".$this->outputName;
  294. $tpl->assign( 'commandDebug' , $command."_DEBUG.exe");
  295. $tpl->assign( 'commandRelease' , $command.".exe");
  296. $tpl->assign( 'commandOptimized' , $command."_OPTIMIZEDDEBUG.exe");
  297. }
  298. else
  299. {
  300. $tpl->assign_by_ref( 'commandDebug' , $this->commandDebug);
  301. $tpl->assign_by_ref( 'commandRelease' , $this->commandRelease);
  302. $tpl->assign_by_ref( 'commandOptimized' , $this->commandOptimized);
  303. }
  304. $tpl->assign_by_ref( 'argsDebug' , $this->argsDebug);
  305. $tpl->assign_by_ref( 'argsRelease' , $this->argsRelease);
  306. $tpl->assign_by_ref( 'argsOptimized' , $this->argsOptimized);
  307. $ptypes = array();
  308. $projectDepends = array();
  309. foreach ($this->dependencies as $pname)
  310. {
  311. $p = Generator::lookupProjectByName( $pname );
  312. $projectDepends[$pname] = $p;
  313. if ( $p )
  314. $ptypes[$pname] = $p->isSharedLib() || $p->isSafari();
  315. }
  316. $tpl->assign_by_ref( 'projTypes', $ptypes );
  317. $tpl->assign_by_ref( 'projectDepends', $projectDepends );
  318. // Assign some handy paths for the template to reference
  319. $tpl->assign( 'projectOffset', $output->project_rel_path );
  320. if ( Generator::$absPath )
  321. $tpl->assign( 'srcDir', Generator::$absPath . "/". str_replace("../", "", getAppEngineSrcDir()) );
  322. else
  323. $tpl->assign( 'srcDir', $output->project_rel_path . getAppEngineSrcDir() );
  324. if ( Generator::$absPath )
  325. $tpl->assign( 'libDir', Generator::$absPath . "/". str_replace("../", "", getAppLibSrcDir()) );
  326. else
  327. $tpl->assign( 'libDir', $output->project_rel_path . getAppLibSrcDir() );
  328. if ( Generator::$absPath )
  329. $tpl->assign( 'binDir', Generator::$absPath . "/". str_replace("../", "", getAppEngineBinDir()) );
  330. else
  331. $tpl->assign( 'binDir', $output->project_rel_path . getAppEngineBinDir() );
  332. $tpl->assign( 'uniformOutputFile', $this->uniformOutputFile);
  333. }
  334. private function conditionDirectories( $output, &$projectFiles )
  335. {
  336. foreach ($this->includes as &$include)
  337. {
  338. if ( !FileUtil::isAbsolutePath( $include ) )
  339. $include = $output->project_rel_path . $include;
  340. }
  341. foreach ($this->lib_dirs as &$libDirs)
  342. {
  343. if ( !FileUtil::isAbsolutePath( $libDirs ) )
  344. $libDirs = $output->project_rel_path . $libDirs;
  345. }
  346. if ( Generator::$absPath )
  347. {
  348. foreach ($this->includes as &$include)
  349. {
  350. if ( stristr($include, getEngineSrcDir()) || stristr($include, getLibSrcDir()) )
  351. $include = Generator::$absPath . "/". str_replace("../", "", $include);
  352. }
  353. foreach ($this->lib_dirs as &$libDirs)
  354. {
  355. if ( stristr($libDirs, getEngineSrcDir()) || stristr($libDirs, getLibSrcDir()) )
  356. $libDirs = Generator::$absPath . "/". str_replace("../", "", $libDirs);
  357. }
  358. }
  359. }
  360. public function generate( $tpl, $platform, $base_dir )
  361. {
  362. // Alright, for each project scan and generate the file list.
  363. $projectFiles = array ();
  364. $rootPhpBuildDir = getcwd();
  365. // Iterate over this project's outputs.
  366. foreach( $this->outputs as $outputName => $output )
  367. {
  368. $saved_includes = $this->includes;
  369. $saved_lib_dirs = $this->lib_dirs;
  370. //print_r( $output );
  371. // Supported platform?
  372. if( !$output->supportsPlatform( $platform ) )
  373. {
  374. //echo( " # Skipping output: '$outputName'.\n" );
  375. continue;
  376. }
  377. // Get to the right working directory (first go back to root, then to relative)
  378. chdir( $base_dir );
  379. //echo( " - Changing CWD to " . $output->output_dir . "\n" );
  380. // echo(" (From: " . getcwd() . ")\n");
  381. if( !FileUtil::prepareOutputDir( $output->output_dir ) )
  382. continue;
  383. //echo( " - Scanning directory for output '.$outputName.'...\n" );
  384. if( !$this->generateFileList( $projectFiles, $outputName, $output ) )
  385. {
  386. echo( "File list generation failed. Giving up on this project.\n" );
  387. continue;
  388. }
  389. // Do any special work on the include/lib directories that we need
  390. $this->conditionDirectories( $output, $projectFiles[ $this->name ] );
  391. $this->projectFileExt = $output->output_ext;
  392. if ( $this->isCSProject() )
  393. $this->projectFileExt = ".csproj"; // always csproj C# project under VS/MonoDevelop
  394. $outfile = $output->project_dir . $this->name . $this->projectFileExt;
  395. echo( " o Writing project file " . $outfile . "\n" );
  396. $this->setTemplateParams( $tpl, $output, $projectFiles[ $this->name ] );
  397. // To put a bandaid on the tools/player output dir problem
  398. // CodeReview: This should be in the template. -- BJG, 3/13/2007
  399. // Moved into templates -- neo
  400. // Write file
  401. $outdir = dirname( $outfile );
  402. if( !file_exists( $outdir ) )
  403. mkdir_r( $outdir, 0777 );
  404. if( $hdl = fopen( $outfile, 'w' ) )
  405. {
  406. if ($this->isApp())
  407. $template = $output->template_app;
  408. else if ($this->isLib())
  409. $template = $output->template_lib;
  410. else if ($this->isSharedLib())
  411. $template = $output->template_shared_lib;
  412. else if ($this->isSharedApp())
  413. $template = $output->template_shared_app;
  414. else if ($this->isActiveX())
  415. $template = $output->template_activex;
  416. else if ($this->isSafari())
  417. $template = $output->template_activex; //rename template?
  418. else if ($this->isCSProject())
  419. $template = $output->template_csproj;
  420. fputs( $hdl, $tpl->fetch( $template ) );
  421. fclose( $hdl );
  422. }
  423. else
  424. trigger_error( "Could not write output file: " . $output->outputFile, E_USER_ERROR );
  425. if ($output->template_user)
  426. {
  427. $outfile = $output->project_dir . $this->name . $this->projectFileExt .'.'.getenv("COMPUTERNAME").'.'.getenv("USERNAME").'.user';
  428. if( !file_exists( $outfile ) )
  429. {
  430. if( $hdl = fopen( $outfile, 'w' ) )
  431. {
  432. $template = $output->template_user;
  433. fputs( $hdl, $tpl->fetch( $template ) );
  434. fclose( $hdl );
  435. }
  436. else
  437. trigger_error( "Could not write output file: " . $outfile, E_USER_ERROR );
  438. }
  439. }
  440. // Build the .filters file used by VS2010.
  441. if ( $output->template_filter )
  442. {
  443. $filterData = new FilterData();
  444. array_walk( $projectFiles[ $this->name ], array($filterData, 'callback'), '' );
  445. $tpl->assign_by_ref('Folders', $filterData->folders);
  446. $tpl->assign_by_ref('SrcFiles', $filterData->srcFiles);
  447. $tpl->assign_by_ref('IncFiles', $filterData->incFiles);
  448. $tpl->assign_by_ref('OtherFiles', $filterData->otherFiles);
  449. $tpl->register_function( 'gen_uuid', 'gen_uuid' );
  450. $outfile = $output->project_dir . $this->name . $this->projectFileExt . '.filters';
  451. if ( $hdl = fopen( $outfile, 'w' ) )
  452. {
  453. fputs( $hdl, $tpl->fetch( $output->template_filter ) );
  454. fclose( $hdl );
  455. }
  456. }
  457. $this->includes = $saved_includes;
  458. $this->lib_dirs = $saved_lib_dirs;
  459. }
  460. }
  461. }
  462. class FilterData
  463. {
  464. public $folders = array();
  465. public $srcFiles = array();
  466. public $incFiles = array();
  467. public $otherFiles = array();
  468. public function callback( $value, $key, $dir )
  469. {
  470. if ( is_array( $value ) )
  471. {
  472. if ( $dir != '' )
  473. $dirpath = $dir . '\\' . $key;
  474. else
  475. $dirpath = $key;
  476. array_push( $this->folders, $dirpath );
  477. array_walk( $value, array($this, 'callback'), $dirpath );
  478. return;
  479. }
  480. $path = str_replace( '/', '\\', $value->path );
  481. $ext = strrchr( $path, '.' );
  482. if ( $ext == FALSE )
  483. return;
  484. if ( strcasecmp( $ext, '.c' ) == 0 ||
  485. strcasecmp( $ext, '.cpp' ) == 0 ||
  486. strcasecmp( $ext, '.cc' ) == 0 )
  487. $this->srcFiles[$path] = $dir;
  488. else if ( strcasecmp( $ext, '.h' ) == 0 ||
  489. strcasecmp( $ext, '.hpp' ) == 0 ||
  490. strcasecmp( $ext, '.inl' ) == 0 )
  491. $this->incFiles[$path] = $dir;
  492. else
  493. $this->otherFiles[$path] = $dir;
  494. }
  495. } // class FilterData
  496. ?>