X-Git-Url: https://vcs.fsf.org/?a=blobdiff_plain;f=xml%2FGenCode.php;h=0bad0ff1511d1733c5dd339078016db55fe291b4;hb=80f86ecfd0116c1288209a06a0be10d1d00ab065;hp=e061552eb404c8d741d0c65f86a8402269865e8a;hpb=3587381cb6fe49cb6628b9348906a6ad3043bec9;p=civicrm-core.git diff --git a/xml/GenCode.php b/xml/GenCode.php index e061552eb4..0bad0ff151 100644 --- a/xml/GenCode.php +++ b/xml/GenCode.php @@ -1,6 +1,18 @@ = 0 and $memLimit < 536870912) { + ini_set('memory_limit', '512M'); +} date_default_timezone_set('UTC'); // avoid php warnings if timezone is not set - CRM-10844 define('CIVICRM_UF', 'Drupal'); @@ -8,12 +20,18 @@ define('CIVICRM_UF', 'Drupal'); require_once 'CRM/Core/ClassLoader.php'; CRM_Core_ClassLoader::singleton()->register(); -$genCode = new CRM_GenCode_Main('../CRM/Core/DAO/', '../sql/', '../', '../templates/'); -$genCode->main( - @$argv[2], - @$argv[3], - empty($argv[1]) ? 'schema/Schema.xml' : $argv[1] +$genCode = new CRM_GenCode_Main( + '../CRM/Core/DAO/', // $CoreDAOCodePath + '../sql/', // $sqlCodePath + '../', // $phpCodePath + '../templates/', // $tplCodePath + array('../packages/Smarty/plugins', '../CRM/Core/Smarty/plugins'), // smarty plugin dirs + @$argv[3], // cms + empty($argv[2]) ? NULL : $argv[2], // db version + empty($argv[1]) ? 'schema/Schema.xml' : $argv[1], // schema file + getenv('CIVICRM_GENCODE_DIGEST') ? getenv('CIVICRM_GENCODE_DIGEST') : NULL // path to digest file ); +$genCode->main(); class CRM_GenCode_Util_File { static function createDir($dir, $perm = 0755) { @@ -38,6 +56,9 @@ class CRM_GenCode_Util_File { } $newTempDir = $tempDir . '/' . $prefix . rand(1, 10000); + if (function_exists('posix_geteuid')) { + $newTempDir .= '_' . posix_geteuid(); + } if (file_exists($newTempDir)) { self::removeDir($newTempDir); @@ -46,30 +67,92 @@ class CRM_GenCode_Util_File { return $newTempDir; } + + /** + * Calculate a cumulative digest based on a collection of files + * + * @param array $files list of file names (strings) + * @param callable $digest a one-way hash function (string => string) + * @return string + */ + static function digestAll($files, $digest = 'md5') { + $buffer = ''; + foreach ($files as $file) { + $buffer .= $digest(file_get_contents($file)); + } + return $digest($buffer); + } + + /** + * Find the path to the main Civi source tree + * + * @return string + * @throws RuntimeException + */ + static function findCoreSourceDir() { + $path = str_replace(DIRECTORY_SEPARATOR, '/', __DIR__); + if (!preg_match(':(.*)/xml:', $path, $matches)) { + throw new RuntimeException("Failed to determine path of code-gen"); + } + + return $matches[1]; + } + + /** + * Find files in several directories using several filename patterns + * + * @param array $pairs each item is an array(0 => $searchBaseDir, 1 => $filePattern) + * @return array of file paths + */ + static function findManyFiles($pairs) { + $files = array(); + foreach ($pairs as $pair) { + list ($dir, $pattern) = $pair; + $files = array_merge($files, CRM_Utils_File::findFiles($dir, $pattern)); + } + return $files; + } } class CRM_GenCode_Main { var $buildVersion; + var $db_version; var $compileDir; var $classNames; + var $cms; // drupal, joomla, wordpress var $CoreDAOCodePath; var $sqlCodePath; var $phpCodePath; var $tplCodePath; + var $schemaPath; // ex: schema/Schema.xml + + /** + * @var string|NULL path in which to store a marker that indicates the last execution of + * GenCode. If a matching marker already exists, GenCode doesn't run. + */ + var $digestPath; + + /** + * @var string|NULL a digest of the inputs to the code-generator (eg the properties and source files) + */ + var $digest; var $smarty; - function __construct($CoreDAOCodePath, $sqlCodePath, $phpCodePath, $tplCodePath) { + function __construct($CoreDAOCodePath, $sqlCodePath, $phpCodePath, $tplCodePath, $smartyPluginDirs, $argCms, $argVersion, $schemaPath, $digestPath) { $this->CoreDAOCodePath = $CoreDAOCodePath; $this->sqlCodePath = $sqlCodePath; $this->phpCodePath = $phpCodePath; $this->tplCodePath = $tplCodePath; + $this->cms = $argCms; + $this->digestPath = $digestPath; + $this->digest = NULL; require_once 'Smarty/Smarty.class.php'; $this->smarty = new Smarty(); $this->smarty->template_dir = './templates'; - $this->smarty->plugins_dir = array('../packages/Smarty/plugins', '../CRM/Core/Smarty/plugins'); + $this->smarty->plugins_dir = $smartyPluginDirs; $this->compileDir = CRM_GenCode_Util_File::createTempDir('templates_c_'); $this->smarty->compile_dir = $this->compileDir; $this->smarty->clear_all_cache(); @@ -91,6 +174,17 @@ class CRM_GenCode_Main { $this->beautifier->setNewLine("\n"); CRM_GenCode_Util_File::createDir($this->sqlCodePath); + + $versionFile = "version.xml"; + $versionXML = &$this->parseInput($versionFile); + $this->db_version = $versionXML->version_no; + $this->buildVersion = preg_replace('/^(\d{1,2}\.\d{1,2})\.(\d{1,2}|\w{4,7})$/i', '$1', $this->db_version); + if (isset($argVersion)) { + // change the version to that explicitly passed, if any + $this->db_version = $argVersion; + } + + $this->schemaPath = $schemaPath; } function __destruct() { @@ -100,20 +194,20 @@ class CRM_GenCode_Main { /** * Automatically generate a variety of files * - * @param $argVersion string, optional - * @param $argCms string, optional; "drupal" or "joomla" - * @param $file, the path to the XML schema file */ - function main($argVersion, $argCms, $file) { - $versionFile = "version.xml"; - $versionXML = &$this->parseInput($versionFile); - $db_version = $versionXML->version_no; - $this->buildVersion = preg_replace('/^(\d{1,2}\.\d{1,2})\.(\d{1,2}|\w{4,7})$/i', '$1', $db_version); - if (isset($argVersion)) { - // change the version to that explicitly passed, if any - $db_version = $argVersion; + function main() { + if (!empty($this->digestPath) && file_exists($this->digestPath) && $this->hasExpectedFiles()) { + if ($this->getDigest() === file_get_contents($this->digestPath)) { + echo "GenCode has previously executed. To force execution, please (a) omit CIVICRM_GENCODE_DIGEST\n"; + echo "or (b) remove {$this->digestPath} or (c) call GenCode with new parameters.\n"; + exit(); + } + // Once we start GenCode, the old build is invalid + unlink($this->digestPath); } - echo "\ncivicrm_domain.version := $db_version\n\n"; + + + echo "\ncivicrm_domain.version := ". $this->db_version . "\n\n"; if ($this->buildVersion < 1.1) { echo "The Database is not compatible for this version"; exit(); @@ -129,12 +223,12 @@ Alternatively you can get a version of CiviCRM that matches your PHP version exit(); } - $this->generateTemplateVersion($argVersion); + $this->generateTemplateVersion(); - $this->setupCms($argCms, $db_version); + $this->setupCms($this->db_version); - echo "Parsing input file $file\n"; - $dbXML = $this->parseInput($file); + echo "Parsing input file ".$this->schemaPath."\n"; + $dbXML = $this->parseInput($this->schemaPath); // print_r( $dbXML ); echo "Extracting database information\n"; @@ -179,17 +273,21 @@ Alternatively you can get a version of CiviCRM that matches your PHP version // $this->generateDropSql($archiveTables, 'civicrm_archive_drop.mysql'); $this->generateNavigation(); - $this->generateLocalDataSql($db_version, $this->findLocales()); + $this->generateLocalDataSql($this->findLocales()); $this->generateSample(); $this->generateInstallLangs(); $this->generateDAOs($tables); $this->generateSchemaStructure($tables); + + if (!empty($this->digestPath)) { + file_put_contents($this->digestPath, $this->getDigest()); + } } function generateListAll($tables) { $this->smarty->clear_all_assign(); $this->smarty->assign('tables', $tables); - file_put_contents($this->CoreDAOCodePath . "../AllCoreTables.php", $this->smarty->fetch('listAll.tpl')); + file_put_contents($this->CoreDAOCodePath . "AllCoreTables.php", $this->smarty->fetch('listAll.tpl')); } function generateCiviTestTruncate($tables) { @@ -232,7 +330,7 @@ Alternatively you can get a version of CiviCRM that matches your PHP version file_put_contents($this->sqlCodePath . "civicrm_navigation.mysql", $this->smarty->fetch('civicrm_navigation.tpl')); } - function generateLocalDataSql($db_version, $locales) { + function generateLocalDataSql($locales) { $this->reset_smarty_assignments(); global $tsLocale; @@ -249,7 +347,7 @@ Alternatively you can get a version of CiviCRM that matches your PHP version $data[] = $this->smarty->fetch('civicrm_data.tpl'); $data[] = $this->smarty->fetch('civicrm_navigation.tpl'); - $data[] = " UPDATE civicrm_domain SET version = '$db_version';"; + $data[] = " UPDATE civicrm_domain SET version = '" . $this->db_version . "';"; $data = implode("\n", $data); @@ -343,16 +441,8 @@ Alternatively you can get a version of CiviCRM that matches your PHP version $this->beautifier->save(); } - function generateTemplateVersion($argVersion) { - // add the Subversion revision to templates - // use svnversion if the version was not specified explicitely on the commandline - if (isset($argVersion) and $argVersion != '') { - $svnversion = $argVersion; - } - else { - $svnversion = `svnversion .`; - } - file_put_contents($this->tplCodePath . "/CRM/common/version.tpl", $svnversion); + function generateTemplateVersion() { + file_put_contents($this->tplCodePath . "/CRM/common/version.tpl", $this->db_version); } function findLocales() { @@ -383,25 +473,56 @@ Alternatively you can get a version of CiviCRM that matches your PHP version return $locales; } - function setupCms($argCms, $db_version) { + function setupCms() { // default cms is 'drupal', if not specified - $cms = isset($argCms) ? strtolower($argCms) : 'drupal'; - if (!in_array($cms, array( - 'drupal', 'joomla'))) { - echo "Config file for '{$cms}' not known."; + $this->cms = isset($this->cms) ? strtolower($this->cms) : 'drupal'; + if (!in_array($this->cms, array( + 'drupal', 'joomla', 'wordpress'))) { + echo "Config file for '{$this->cms}' not known."; exit(); } - elseif ($cms !== 'joomla') { - echo "Generating civicrm.config.php\n"; - copy("../{$cms}/civicrm.config.php.{$cms}", '../civicrm.config.php'); + elseif ($this->cms !== 'joomla') { + $configTemplate = $this->findConfigTemplate($this->cms); + if ($configTemplate) { + echo "Generating civicrm.config.php\n"; + copy($configTemplate, '../civicrm.config.php'); + } else { + throw new Exception("Failed to locate template for civicrm.config.php"); + } } echo "Generating civicrm-version file\n"; - $this->smarty->assign('db_version', $db_version); - $this->smarty->assign('cms', ucwords($cms)); + $this->smarty->assign('db_version', $this->db_version); + $this->smarty->assign('cms', ucwords($this->cms)); file_put_contents($this->phpCodePath . "civicrm-version.php", $this->smarty->fetch('civicrm_version.tpl')); } + /** + * @param string $cms "drupal"|"wordpress" + * @return null|string path to config template + */ + public function findConfigTemplate($cms) { + $candidates = array(); + switch ($cms) { + case 'drupal': + $candidates[] = "../drupal/civicrm.config.php.drupal"; + $candidates[] = "../../drupal/civicrm.config.php.drupal"; + break; + case 'wordpress': + $candidates[] = "../../civicrm.config.php.wordpress"; + $candidates[] = "../WordPress/civicrm.config.php.wordpress"; + $candidates[] = "../drupal/civicrm.config.php.drupal"; + break; + } + foreach ($candidates as $candidate) { + if (file_exists($candidate)) { + return $candidate; + break; + } + } + return NULL; + } + // ----------------------------- // ---- Schema manipulation ---- // ----------------------------- @@ -684,6 +805,7 @@ Alternatively you can get a version of CiviCRM that matches your PHP version } $field['required'] = $this->value('required', $fieldXML); + $field['collate'] = $this->value('collate', $fieldXML); $field['comment'] = $this->value('comment', $fieldXML); $field['default'] = $this->value('default', $fieldXML); $field['import'] = $this->value('import', $fieldXML); @@ -702,15 +824,33 @@ Alternatively you can get a version of CiviCRM that matches your PHP version $field['dataPattern'] = $this->value('dataPattern', $fieldXML); $field['uniqueName'] = $this->value('uniqueName', $fieldXML); $field['pseudoconstant'] = $this->value('pseudoconstant', $fieldXML); - if(!empty($fieldXML->pseudoconstant)){ + if(!empty($field['pseudoconstant'])){ //ok this is a bit long-winded but it gets there & is consistent with above approach $field['pseudoconstant'] = array(); - $validOptions = array('name', 'optionGroupName', 'table', 'keyColumn', 'labelColumn','class'); - foreach ($validOptions as $pseudoOption){ + $validOptions = array( + // Fields can specify EITHER optionGroupName OR table, not both + // (since declaring optionGroupName means we are using the civicrm_option_value table) + 'optionGroupName', + 'table', + // If table is specified, keyColumn and labelColumn are also required + 'keyColumn', + 'labelColumn', + // Non-translated machine name for programmatic lookup. Defaults to 'name' if that column exists + 'nameColumn', + // Where clause snippet (will be joined to the rest of the query with AND operator) + 'condition', + ); + foreach ($validOptions as $pseudoOption) { if(!empty($fieldXML->pseudoconstant->$pseudoOption)){ $field['pseudoconstant'][$pseudoOption] = $this->value($pseudoOption, $fieldXML->pseudoconstant); } } + // For now, fields that have option lists that are not in the db can simply + // declare an empty pseudoconstant tag and we'll add this placeholder. + // That field's BAO::buildOptions fn will need to be responsible for generating the option list + if (empty($field['pseudoconstant'])) { + $field['pseudoconstant'] = 'not in database'; + } } $fields[$name] = &$field; } @@ -936,4 +1076,55 @@ Alternatively you can get a version of CiviCRM that matches your PHP version $this->smarty->clear_all_cache(); $this->smarty->assign('generated', "DO NOT EDIT. Generated by " . basename(__FILE__)); } + + + /** + * Compute a digest based on the inputs to the code-generator (ie the properties + * of the codegen and the source files loaded by the codegen). + * + * @return string + */ + function getDigest() { + if ($this->digest === NULL) { + $srcDir = CRM_GenCode_Util_File::findCoreSourceDir(); + $files = CRM_GenCode_Util_File::findManyFiles(array( + // array("$srcDir/CRM/Core/CodeGen", '*.php'), + array("$srcDir/xml", "*.php"), + array("$srcDir/xml", "*.tpl"), + array("$srcDir/xml", "*.xml"), + )); + + $properties = var_export(array( + CRM_GenCode_Util_File::digestAll($files), + $this->buildVersion, + $this->db_version, + $this->cms, + $this->CoreDAOCodePath, + $this->sqlCodePath, + $this->phpCodePath, + $this->tplCodePath, + $this->schemaPath, + // $this->getTasks(), + ), TRUE); + + $this->digest = md5($properties); + } + return $this->digest; + } + + function getExpectedFiles() { + return array( + $this->sqlCodePath . '/civicrm.mysql', + $this->phpCodePath . '/CRM/Contact/DAO/Contact.php', + ); + } + + function hasExpectedFiles() { + foreach ($this->getExpectedFiles() as $file) { + if (!file_exists($file)) { + return FALSE; + } + } + return TRUE; + } }