AddDependentPackage() { var packageNameArgument = new Options.PackageName(); var packageName = CommandLineProcessor.Evaluate(packageNameArgument); if (null == packageName) { throw new Exception("No name was defined. Use {0} on the command line to specify it.", (packageNameArgument as ICommandLineArgument).LongName); } var packageVersion = CommandLineProcessor.Evaluate(new Options.PackageVersion()); var masterPackage = GetMasterPackage(); if (null != masterPackage.Dependents.FirstOrDefault(item => item.Item1 == packageName && item.Item2 == packageVersion)) { if (null != packageVersion) { throw new Exception("Package dependency {0}, version {1}, is already present", packageName, packageVersion); } else { throw new Exception("Package dependency {0} is already present", packageName); } } var newDepTuple = new System.Tuple <string, string, bool?>(packageName, packageVersion, null); masterPackage.Dependents.Add(newDepTuple); // TODO: this is unfortunate having to write the file in order to use it with IdentifyAllPackages masterPackage.Write(); // validate that the addition is ok try { PackageUtilities.IdentifyAllPackages(); } catch (Exception exception) { masterPackage.Dependents.Remove(newDepTuple); masterPackage.Write(); throw new Exception(exception, "Failed to add dependent. Are all necessary package repositories specified?"); } }
Execute( Array <Environment> environments, System.Reflection.Assembly packageAssembly = null) { PrintVersion(); if (0 == environments.Count) { throw new Exception("No build configurations were specified"); } var graph = Graph.Instance; if (null != packageAssembly) { PackageUtilities.IdentifyAllPackages(); graph.ScriptAssembly = packageAssembly; graph.ScriptAssemblyPathname = packageAssembly.Location; } else { PackageUtilities.CompilePackageAssembly(); PackageUtilities.LoadPackageAssembly(); } var packageMetaDataProfile = new TimeProfile(ETimingProfiles.PackageMetaData); packageMetaDataProfile.StartProfile(); // validate that there is at most one local policy // if test mode is enabled, then the '.tests' sub-namespaces are also checked { var localPolicies = graph.ScriptAssembly.GetTypes().Where(t => typeof(ISitePolicy).IsAssignableFrom(t)); var includeTests = CommandLineProcessor.Evaluate(new Options.UseTests()); if (!includeTests) { localPolicies = localPolicies.Where(item => !item.Namespace.EndsWith(".tests")); } var numLocalPolicies = localPolicies.Count(); if (numLocalPolicies > 0) { if (numLocalPolicies > 1) { var message = new System.Text.StringBuilder(); message.AppendLine("Too many site policies exist in the package assembly:"); foreach (var policy in localPolicies) { message.AppendFormat("\t{0}", policy.ToString()); message.AppendLine(); } throw new Exception(message.ToString()); } Settings.LocalPolicy = System.Activator.CreateInstance(localPolicies.First()) as ISitePolicy; } } // find a product definition { var productDefinitions = graph.ScriptAssembly.GetTypes().Where(t => typeof(IProductDefinition).IsAssignableFrom(t)); var numProductDefinitions = productDefinitions.Count(); if (numProductDefinitions > 0) { if (numProductDefinitions > 1) { var message = new System.Text.StringBuilder(); message.AppendLine("Too many product definitions exist in the package assembly:"); foreach (var def in productDefinitions) { message.AppendFormat("\t{0}", def.ToString()); message.AppendLine(); } throw new Exception(message.ToString()); } graph.ProductDefinition = System.Activator.CreateInstance(productDefinitions.First()) as IProductDefinition; } } // get the metadata from the build mode package var metaName = System.String.Format("{0}Builder.{0}Meta", graph.Mode); var metaDataType = graph.ScriptAssembly.GetType(metaName); if (null == metaDataType) { throw new Exception("No build mode {0} meta data type {1}", graph.Mode, metaName); } if (!typeof(IBuildModeMetaData).IsAssignableFrom(metaDataType)) { throw new Exception("Build mode package meta data type {0} does not implement the interface {1}", metaDataType.ToString(), typeof(IBuildModeMetaData).ToString()); } graph.BuildModeMetaData = System.Activator.CreateInstance(metaDataType) as IBuildModeMetaData; // packages can have meta data - instantiate where they exist foreach (var package in graph.Packages) { var ns = package.Name; var metaType = graph.ScriptAssembly.GetTypes().FirstOrDefault(item => item.Namespace == ns && typeof(PackageMetaData).IsAssignableFrom(item)); if (null == metaType) { continue; } try { package.MetaData = System.Activator.CreateInstance(metaType) as PackageMetaData; } catch (Exception exception) { throw exception; } catch (System.Reflection.TargetInvocationException exception) { throw new Exception(exception, "Failed to create package metadata"); } } packageMetaDataProfile.StopProfile(); var topLevelNamespace = graph.MasterPackage.Name; var findBuildableModulesProfile = new TimeProfile(ETimingProfiles.IdentifyBuildableModules); findBuildableModulesProfile.StartProfile(); // Phase 1: Instantiate all modules in the namespace of the package in which the tool was invoked Log.Detail("Creating modules"); foreach (var env in environments) { graph.CreateTopLevelModules(graph.ScriptAssembly, env, topLevelNamespace); } findBuildableModulesProfile.StopProfile(); var populateGraphProfile = new TimeProfile(ETimingProfiles.PopulateGraph); populateGraphProfile.StartProfile(); // Phase 2: Graph now has a linear list of modules; create a dependency graph // NB: all those modules with 0 dependees are the top-level modules // NB: default settings have already been defined here // not only does this generate the dependency graph, but also creates the default settings for each module, and completes them graph.SortDependencies(); populateGraphProfile.StopProfile(); // TODO: make validation optional, if it starts showing on profiles var validateGraphProfile = new TimeProfile(ETimingProfiles.ValidateGraph); validateGraphProfile.StartProfile(); graph.Validate(); validateGraphProfile.StopProfile(); // Phase 3: (Create default settings, and ) apply patches (build + shared) to each module // NB: some builders can use the patch directly for child objects, so this may be dependent upon the builder // Toolchains for modules need to be set here, as they might append macros into each module in order to evaluate paths // TODO: a parallel thread can be spawned here, that can check whether command lines have changed // the Settings object can be inspected, and a hash generated. This hash can be written to disk, and compared. // If a 'verbose' mode is enabled, then more work can be done to figure out what has changed. This would also require // serializing the binary Settings object var createPatchesProfile = new TimeProfile(ETimingProfiles.CreatePatches); createPatchesProfile.StartProfile(); graph.ApplySettingsPatches(); createPatchesProfile.StopProfile(); // expand paths after patching settings, because some of the patches may contain tokenized strings // TODO: a thread can be spawned, to check for whether files were in date or not, which will // be ready in time for graph execution var parseStringsProfile = new TimeProfile(ETimingProfiles.ParseTokenizedStrings); parseStringsProfile.StartProfile(); TokenizedString.ParseAll(); parseStringsProfile.StopProfile(); if (CommandLineProcessor.Evaluate(new Options.ViewDependencyGraph())) { // must come after all strings are parsed, in order to display useful paths graph.Dump(); } // Phase 4: Execute dependency graph // N.B. all paths (including those with macros) have been delayed expansion until now var graphExecutionProfile = new TimeProfile(ETimingProfiles.GraphExecution); graphExecutionProfile.StartProfile(); var executor = new Executor(); executor.Run(); graphExecutionProfile.StopProfile(); }