/// <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));
        }
Exemple #2
0
        /// <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 !);
        }
Exemple #3
0
        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);
        }