/// <summary> /// Calls <see cref="AddStObjMap(IServiceCollection, IActivityMonitor, IStObjMap, SimpleServiceContainer)"/> after /// having obtained the map with <see cref="StObjContextRoot.Load(Assembly, IActivityMonitor)"/>. /// <para> /// Assembly load conflicts may occur here. In such case, you should use the CK.WeakAssemblyNameResolver package /// and wrap the call this way: /// <code> /// using( CK.Core.WeakAssemblyNameResolver.TemporaryInstall() ) /// { /// services.AddStObjMap( "CK.StObj.AutoAssembly" ); /// } /// </code> /// Note that there SHOULD NOT be any conflicts. This workaround may be necessary but hides a conflict of version dependencies /// that may cause runtime errors. /// </para> /// <para> /// If the registration fails for any reason (file not found, type conflicts, etc.), an <see cref="InvalidOperationException"/> is thrown. /// </para> /// </summary> /// <param name="services">This services.</param> /// <param name="monitor">Monitor to use.</param> /// <param name="assemblyName">The assembly name (with or without '.dll' or '.exe' suffix).</param> /// <param name="startupServices"> /// Optional simple container that may provide startup services. This is not used to build IRealObject /// (they must be independent of any "dynamic" services), however registered services become available to /// any <see cref="StObjContextRoot.ConfigureServicesMethodName"/> methods by parameter injection. /// </param> /// <returns>This services collection.</returns> public static IServiceCollection AddStObjMap(this IServiceCollection services, IActivityMonitor monitor, string assemblyName, SimpleServiceContainer?startupServices = null) { var map = StObjContextRoot.Load(assemblyName, monitor); if (map == null) { throw new ArgumentException($"The assembly '{assemblyName}' was not found or is not a valid generated assembly."); } return(AddStObjMap(services, monitor, map, startupServices)); }
/// <summary> /// Loads the <see cref="IStObjMap"/> from <see cref="IRunningBinPathGroup.RunSignature"/> SHA1 from /// already available maps (see <see cref="StObjContextRoot.Load(SHA1Value, IActivityMonitor?)"/>) /// or from the <see cref="IRunningBinPathGroup.GeneratedAssembly"/>. /// <para> /// This must not be called on the <see cref="IRunningBinPathGroup.IsUnifiedPure"/> otherwise an <see cref="InvalidOperationException"/> /// is thrown. /// </para> /// </summary> /// <param name="g">This group from which the map must be obtain.</param> /// <param name="embeddedIfPossible"> /// False to skip an available map and load it from the generated assembly. /// By default, the map is searched in available ones before loading the assembly. /// </param> /// <returns>The map.</returns> public static IStObjMap LoadStObjMap(this IRunningBinPathGroup g, bool embeddedIfPossible = true) { Throw.CheckState(!g.IsUnifiedPure); IStObjMap?map = null; if (embeddedIfPossible) { IStObjMap?embedded = StObjContextRoot.Load(g.RunSignature, TestHelper.Monitor); if (embedded != null) { TestHelper.Monitor.Info(embedded == null ? "No embedded generated source code." : "Embedded generated source code is available."); } } if (map == null) { g.GeneratedAssembly.Exists().Should().BeTrue($"The assembly '{g.GeneratedAssembly.Path}' should have been generated."); var a = AssemblyLoadContext.Default.LoadFromAssemblyPath(g.GeneratedAssembly.Path); map = StObjContextRoot.Load(a, TestHelper.Monitor); map.Should().NotBeNull($"The assembly '{g.GeneratedAssembly.Path}' should be loadable."); } return(map !); }
internal bool Initialize(IActivityMonitor monitor, bool forceRun, ref bool canSkipRun) { if (IsUnifiedPure) { // If we are on the unified pure BinPath. Debug.Assert(_saveSource == SaveSourceLevel.None && CompileOption == CompileOption.None && SimilarConfigurations.Single() == Configuration && RunSignature.IsZero); return(true); } CompileOption compile = CompileOption.None; bool source = false; foreach (var b in SimilarConfigurations) { compile = (CompileOption)Math.Max((int)compile, (int)b.CompileOption); source |= b.GenerateSourceFiles; } CompileOption = compile; _saveSource = source ? SaveSourceLevel.SaveSource : SaveSourceLevel.None; if (RunSignature.IsZero) { // No known code base SHA1. // We are not called by CKSetup: the StObjEngine is run in-process, typically // by CK.Testing.StObjEngine. // Retrieving the SHA1 (if forceSetup is false) from the existing generated source and/or assembly // is easily doable but pointless: when the StObjEngine is ran in-process without known SHA1, it is // with a StObjCollector (the set of types) that is specific and with no way to have any clue about // their "content" (even for two consecutive identical set of types, their code, attributes or the // code of the generators may have changed between 2 runs). // In this usage, the goal is to correctly manage the G0.cs and CK.StObj.AutoAssembly files. // // The behavior here is tailored for CK.Testing.StObjEngine and by its API. // If the source code is not required, we require it here so that the SHA1 can be computed based on // the generated code source. if (_saveSource == SaveSourceLevel.None) { monitor.Info($"Source code for '{Names}' will be generated to compute the SHA1 but will not be saved."); // This level doesn't need to be exposed since the GenerateSourceCodeSecondPass // will generate the source code even if SaveSource is false, CompileOption is None as soon as RunSignature // is zero: this level is here to avoid setting SaveSource to true here so that CopyArtifactsFromHead // will not update any files. _saveSource = SaveSourceLevel.RequiredForSHA1; } canSkipRun = false; } else if (!forceRun && (_saveSource != SaveSourceLevel.None || compile != CompileOption.None)) { // A code base SHA1 is provided. // If we can find this map in the already available StObjMap, we may skip the run. var mapInfo = StObjContextRoot.GetMapInfo(RunSignature, monitor); if (mapInfo != null) { monitor.Info($"An existing StObjMap with the signature is already loaded: setting SaveSource to false and CompileOption to None for BinPaths {Names}."); _saveSource = SaveSourceLevel.None; CompileOption = CompileOption.None; } else { if (_saveSource != SaveSourceLevel.None && GeneratedSource.GetSignature(monitor) == RunSignature) { monitor.Info($"Source '{GeneratedSource}' is up to date. Setting SaveSource to false for BinPaths {Names}."); _saveSource = SaveSourceLevel.None; } if (compile != CompileOption.None && GeneratedAssembly.GetSignature(monitor) == RunSignature) { monitor.Info($"Assembly '{GeneratedAssembly}' is up to date. Setting CompileOption to None for BinPaths {Names}."); CompileOption = CompileOption.None; } } canSkipRun &= _saveSource == SaveSourceLevel.None && CompileOption == CompileOption.None; } return(true); }