public static bool Execute(bool isSecondPass, string flagsString, ILogger logger)
        {
            string passNumber    = (isSecondPass ? "2" : "1");
            string operationName = string.Format("C#/XAML for HTML5: AfterXamlPreprocessor (pass {0})", passNumber);

            try
            {
                using (var executionTimeMeasuring = new ExecutionTimeMeasuring())
                {
                    //------- DISPLAY THE PROGRESS -------
                    logger.WriteMessage(operationName + " started.");

                    //-----------------------------------------------------
                    // Note: we dispose the static instance of the "ReflectionOnSeparateAppDomainHandler" that was created in the "BeforeXamlPreprocessor" task.
                    // Disposing it allows to free any hooks on the user app DLL's.
                    //-----------------------------------------------------

                    // Dispose the static instance of the "ReflectionOnSeparateAppDomainHandler":
                    ReflectionOnSeparateAppDomainHandler.Current.Dispose(); // Note: this is not supposed to be null because it was instantiated in the "BeforeXamlPreprocessor" task.


                    bool isSuccess = true;

                    //------- DISPLAY THE PROGRESS -------
                    logger.WriteMessage(operationName + (isSuccess ? " completed in " + executionTimeMeasuring.StopAndGetTimeInSeconds() + " seconds." : " failed."));

                    return(isSuccess);
                }
            }
            catch (Exception ex)
            {
                logger.WriteError(operationName + " failed: " + ex.ToString());
                return(false);
            }
        }
        public static bool Execute(
            string[] sourceLibrariesDirectories,
            string pathOfAssemblyThatContainsEntryPoint,
            ILogger logger,
            string outputRootPath,
            string outputAppFilesPath,
            string outputLibrariesPath,
            string outputResourcesPath,
            string[] listOfResXGeneratedFiles,
            string assemblyNameWithoutExtension
            )
        {
            string operationName = "C#/XAML for HTML5: Wrapper";

            try
            {
                using (var executionTimeMeasuring = new ExecutionTimeMeasuring())
                {
                    // Validate input strings:
                    if (string.IsNullOrEmpty(pathOfAssemblyThatContainsEntryPoint))
                    {
                        throw new Exception(operationName + " failed because the entry point assembly path is invalid.");
                    }

                    //------- DISPLAY THE PROGRESS -------
                    logger.WriteMessage(operationName + " started. Source libraries directories: \"" + string.Join(";", sourceLibrariesDirectories) + "\". Assembly that contains entry point: \"" + pathOfAssemblyThatContainsEntryPoint);
                    //todo: do not display that much info.

                    // Start copying support HTML and JavaScript files:
                    WrappingOutputFiles_BridgeVersion.CreateAndCopySupportFiles(
                        pathOfAssemblyThatContainsEntryPoint,
                        new List <string>(sourceLibrariesDirectories),
                        outputRootPath,
                        outputAppFilesPath,
                        outputLibrariesPath,
                        outputResourcesPath,
                        listOfResXGeneratedFiles,
                        assemblyNameWithoutExtension
                        );

                    //------- DISPLAY THE PROGRESS -------
                    logger.WriteMessage(operationName + " completed in " + executionTimeMeasuring.StopAndGetTimeInSeconds() + " seconds.");

                    return(true);
                }
            }
            catch (Exception ex)
            {
                logger.WriteError(operationName + " failed: " + ex.ToString());
                return(false);
            }
        }
Example #3
0
        public static bool Execute(bool isSecondPass, string flagsString, string referencePathsString, string sourceAssemblyForPass2, string nameOfAssembliesThatDoNotContainUserCode, bool isBridgeBasedVersion, bool isProcessingCSHTML5Itself, ILogger logger, string typeForwardingAssemblyPath)
#endif

        {
            string passNumber    = (isSecondPass ? "2" : "1");
            string operationName = string.Format("C#/XAML for HTML5: BeforeXamlPreprocessor (pass {0})", passNumber);

            try
            {
                using (var executionTimeMeasuring = new ExecutionTimeMeasuring())
                {
                    //------- DISPLAY THE PROGRESS -------
                    logger.WriteMessage(operationName + " started.");

                    //-----------------------------------------------------
                    // Note: we create a static instance of the "ReflectionOnSeparateAppDomainHandler" to avoid reloading the assemblies for each XAML file.
                    // We dispose the static instance in the "AfterXamlPreprocessor" task.
                    //-----------------------------------------------------

                    if (isSecondPass && string.IsNullOrEmpty(sourceAssemblyForPass2))
                    {
                        throw new Exception(operationName + " failed because the SourceAssembly parameter was not specified during the second pass.");
                    }

                    // Create a new static instance of the "ReflectionOnSeparateAppDomainHandler":
                    ReflectionOnSeparateAppDomainHandler.Current = new ReflectionOnSeparateAppDomainHandler(typeForwardingAssemblyPath);
                    ReflectionOnSeparateAppDomainHandler reflectionOnSeparateAppDomain = ReflectionOnSeparateAppDomainHandler.Current;

#if BRIDGE
                    //todo: if we are compiling CSHTML5 itself (or CSHTML5.Stubs), we need to process the XAML files in CSHTML5,
                    // and for that we need to load the XAML types, so we need to load the previous version of CSHTML5 (from
                    // the NuGet package). Note: this is not supposed to lead to a circular reference because it is only used
                    // for the XamlPreprocessor to generate the .xaml.g.cs files from the .xaml files.
                    // To do so, we need to stop skipping the processing of the CSHTML5 and CSHTML5.Stubs assemblies (c.f.
                    // "Skip the assembly if it is not a user assembly" in "LoadAndProcessReferencedAssemblies").
#endif
                    // we load the source assembly early in case we are processing the CSHTML5.
                    if (isSecondPass && isProcessingCSHTML5Itself)
                    {
                        reflectionOnSeparateAppDomain.LoadAssembly(sourceAssemblyForPass2, loadReferencedAssembliesToo: true, isBridgeBasedVersion: isBridgeBasedVersion, isCoreAssembly: false, nameOfAssembliesThatDoNotContainUserCode: nameOfAssembliesThatDoNotContainUserCode);
                    }
#if CSHTML5BLAZOR
                    // work-around: reference path string is not correctly setted so we set it manually
                    string referencePathsString = OpenSilverHelper.ReferencePathsString(resolvedReferences);
#endif
                    // Retrieve paths of referenced .dlls and load them:
                    HashSet <string> referencePaths = (referencePathsString != null) ? new HashSet <string>(referencePathsString.Split(';')) : new HashSet <string>();

                    referencePaths.RemoveWhere(s => !s.ToLower().EndsWith(".dll") || s.Contains("DotNetBrowser") || s.ToLower().EndsWith(@"\bridge.dll"));

                    foreach (string referencedAssembly in AssembliesLoadHelper.EnsureCoreAssemblyIsFirstInList(referencePaths)) // Note: we ensure that the Core assembly is loaded first so that types such as "XmlnsDefinitionAttribute" are known when loading the other assemblies.
                    {
                        reflectionOnSeparateAppDomain.LoadAssembly(referencedAssembly, loadReferencedAssembliesToo: false, isBridgeBasedVersion: isBridgeBasedVersion, isCoreAssembly: false, nameOfAssembliesThatDoNotContainUserCode: nameOfAssembliesThatDoNotContainUserCode);
                    }

                    // Load "mscorlib.dll" too (this is useful for resolving Mscorlib types in XAML, such as <system:String x:Key="TestString" xmlns:system="clr-namespace:System;assembly=mscorlib">Test</system:String>)
                    reflectionOnSeparateAppDomain.LoadAssemblyMscorlib(isBridgeBasedVersion: isBridgeBasedVersion, isCoreAssembly: false, nameOfAssembliesThatDoNotContainUserCode: nameOfAssembliesThatDoNotContainUserCode);

                    // Load for reflection the source assembly itself and the referenced assemblies if second path:
                    if (isSecondPass && !isProcessingCSHTML5Itself)
                    {
                        reflectionOnSeparateAppDomain.LoadAssembly(sourceAssemblyForPass2, loadReferencedAssembliesToo: true, isBridgeBasedVersion: isBridgeBasedVersion, isCoreAssembly: false, nameOfAssembliesThatDoNotContainUserCode: nameOfAssembliesThatDoNotContainUserCode);
                    }

                    bool isSuccess = true;

                    //------- DISPLAY THE PROGRESS -------
                    logger.WriteMessage(operationName + (isSuccess ? " completed in " + executionTimeMeasuring.StopAndGetTimeInSeconds() + " seconds." : " failed.") + "\". IsSecondPass: "******". Source assembly file: \"" + (sourceAssemblyForPass2 ?? "").ToString());

                    return(isSuccess);
                }
            }
            catch (Exception ex)
            {
                if (ReflectionOnSeparateAppDomainHandler.Current != null)
                {
                    ReflectionOnSeparateAppDomainHandler.Current.Dispose();
                }

                logger.WriteError(operationName + " failed: " + ex.ToString());
                return(false);
            }
        }
Example #4
0
        public static bool Execute(string sourceAssembly, string outputRootPath, string outputAppFilesPath, string outputLibrariesPath, string outputResourcesPath, ILogger logger)
        {
            string operationName = "C#/XAML for HTML5: OutputPathsEraser";

            try
            {
                using (var executionTimeMeasuring = new ExecutionTimeMeasuring())
                {
                    // Validate input strings:
                    if (string.IsNullOrEmpty(sourceAssembly))
                    {
                        throw new Exception(operationName + " failed because the source assembly argument is invalid.");
                    }

                    //------- DISPLAY THE PROGRESS -------
                    logger.WriteMessage(operationName + " started for assembly \"" + sourceAssembly + "\".");

                    // Determine the absolute output path:
                    string outputPathAbsolute = PathsHelper.GetOutputPathAbsolute(sourceAssembly, outputRootPath);

                    // Create the output directory if it does not already exist:
                    Directory.CreateDirectory(Path.GetDirectoryName(outputPathAbsolute));

                    // Verify that the paths are relative, not absolute, because the HTML app will use them at runtime to locate the resources, so they must be located in a subfolder of the output folder. Futhermore, this reduces the chances of accidentally deleting other user files:
                    if (Path.IsPathRooted(outputAppFilesPath))
                    {
                        throw new Exception("The output app files path ('Cshtml5OutputAppFilesPath') must be relative, not absolute: " + outputAppFilesPath);
                    }
                    if (Path.IsPathRooted(outputLibrariesPath))
                    {
                        throw new Exception("The output libraries path ('Cshtml5OutputLibrariesPath') must be relative, not absolute: " + outputLibrariesPath);
                    }
                    if (Path.IsPathRooted(outputResourcesPath))
                    {
                        throw new Exception("The output resources path ('Cshtml5OutputResourcesPath') must be relative, not absolute: " + outputResourcesPath);
                    }

                    // Determine the absolute path of the AppFiles, Libraries, and Resources subfolders of the output folder:
                    string absoluteOutputAppFilesPath  = PathsHelper.CombinePathsWhileEnsuringEndingBackslashAndMore(outputPathAbsolute, outputAppFilesPath);
                    string absoluteOutputLibrariesPath = PathsHelper.CombinePathsWhileEnsuringEndingBackslashAndMore(outputPathAbsolute, outputLibrariesPath);
                    string absoluteOutputResourcesPath = PathsHelper.CombinePathsWhileEnsuringEndingBackslashAndMore(outputPathAbsolute, outputResourcesPath);

                    // Make sure that the AppFiles, Libraries, and Resources subfolders of the output directories are empty:
                    if (Directory.Exists(absoluteOutputLibrariesPath))
                    {
                        FileHelpers.DeleteAllFilesAndFoldersInDirectory(absoluteOutputLibrariesPath); //todo: Display a warning in case that the user has specified an output path that contains stuff other than ours, such as "C:\"?
                    }
                    if (Directory.Exists(absoluteOutputResourcesPath))
                    {
                        FileHelpers.DeleteAllFilesAndFoldersInDirectory(absoluteOutputResourcesPath); //todo: Display a warning in case that the user has specified an output path that contains stuff other than ours, such as "C:\"?
                    }

                    /*
                     * NOTE: The following line was commented on October 24, 2017. In fact, starting in CSHTML5 Beta 12.3, we no longer clean up the "app" folder on recompilation, in order to speed the compilation up by re-using the assemblies that have already been compiled.
                     * if (Directory.Exists(absoluteOutputAppFilesPath))
                     *  FileHelpers.DeleteAllFilesAndFoldersInDirectory(absoluteOutputAppFilesPath);
                     */

                    //------- DISPLAY THE PROGRESS -------
                    logger.WriteMessage(operationName + " completed in " + executionTimeMeasuring.StopAndGetTimeInSeconds() + " seconds.");

                    return(true);
                }
            }
            catch (Exception ex)
            {
                logger.WriteError(operationName + " failed: " + ex.ToString());
                return(false);
            }
        }
Example #5
0
        public static bool Execute(string sourceFile, string outputFile, string fileNameWithPathRelativeToProjectRoot, string assemblyNameWithoutExtension, string coreAssemblyFiles, bool isSecondPass, bool isSLMigration, ILogger logger, string activationAppPath, string cSharpXamlForHtml5OutputType, bool overrideOutputOnlyIfSourceHasChanged, string outputRootPath, string outputAppFilesPath, string outputLibrariesPath, string outputResourcesPath, string flagsString, bool isBridgeBasedVersion, string nameOfAssembliesThatDoNotContainUserCode)
        {
            string passNumber    = (isSecondPass ? "2" : "1");
            string operationName = string.Format("C#/XAML for HTML5: XamlPreprocessor (pass {0})", passNumber);

            try
            {
                using (var executionTimeMeasuring = new ExecutionTimeMeasuring())
                {
                    // Validate input strings:
                    if (string.IsNullOrEmpty(sourceFile))
                    {
                        throw new Exception(operationName + " failed because the source file argument is invalid.");
                    }
                    if (string.IsNullOrEmpty(outputFile))
                    {
                        throw new Exception(operationName + " failed because the output file argument is invalid.");
                    }
                    if (string.IsNullOrEmpty(fileNameWithPathRelativeToProjectRoot))
                    {
                        throw new Exception(operationName + " failed because the FileNameWithPathRelativeToProjectRoot argument is invalid.");
                    }
                    if (string.IsNullOrEmpty(assemblyNameWithoutExtension))
                    {
                        throw new Exception(operationName + " failed because the AssemblyNameWithoutExtension argument is invalid.");
                    }
                    if (string.IsNullOrEmpty(coreAssemblyFiles))
                    {
                        throw new Exception(operationName + " failed because the core assembly file argument is invalid.");
                    }

                    HashSet <string> flags = (flagsString != null ? new HashSet <string>(flagsString.Split(';')) : new HashSet <string>());

#if REQUIRE_ACTIVATION_FOR_SILVERLIGHT_MIGRATION
#if SILVERLIGHTCOMPATIBLEVERSION
#if !BRIDGE
                    //------- Check SL Migration license (unless we are compiling a class library, in which case we do not want to check so that it is more convenient for developing Extensions that work with any CSHTML5 edition) -------
                    if (cSharpXamlForHtml5OutputType == null || cSharpXamlForHtml5OutputType.ToLower() != "library")
                    {
                        if (!CheckSLMigrationLicense(logger, activationAppPath, flags))
                        {
                            return(false);
                        }
                    }
#endif
#endif
#endif

                    //------- DISPLAY THE PROGRESS -------
                    logger.WriteMessage(operationName + " started for file \"" + sourceFile + "\". Output file: \"" + outputFile + "\". FileNameWithPathRelativeToProjectRoot: \"" + fileNameWithPathRelativeToProjectRoot + "\". AssemblyNameWithoutExtension: \"" + assemblyNameWithoutExtension + "\". Core assembly files: \"" + coreAssemblyFiles + "\". IsSecondPass: "******"\".");
                    //todo: do not display the output file location?

                    // Read the XAML file:
                    using (StreamReader sr = new StreamReader(sourceFile))
                    {
                        String xaml = sr.ReadToEnd();

                        // Determine if the file should be processed or if there is no need to process it again (for example if the XAML has not changed and we are in design-time, we don't want to re-process the XAML):
                        bool shouldTheFileBeProcessed = DetermineIfTheXamlFileNeedsToBeProcessed(xaml, outputFile, overrideOutputOnlyIfSourceHasChanged, isSecondPass);

                        if (shouldTheFileBeProcessed)
                        {
                            // The "ReflectionOnSeparateAppDomainHandler" class lets us use a separate AppDomain to resolve the types so that the types can be unloaded when done (when disposed, it frees any hook on the user application DLL's):
                            ReflectionOnSeparateAppDomainHandler reflectionOnSeparateAppDomain = ReflectionOnSeparateAppDomainHandler.Current; // Note: this is not supposed to be null because it was instantiated in the "BeforeXamlPreprocessor" task. We use a static instance to avoid reloading the assemblies for each XAML file that is processed.

                            // Make sure that the reference is not null:
                            if (reflectionOnSeparateAppDomain == null)
                            {
                                throw new Exception("ReflectionOnSeparateAppDomainHandler.Current is null. It should not be null because it was supposed to be populated by the 'BeforeXamlPreprocessor' task. Please verify that the MSBuild Targets are up to date.");
                            }

                            // Convert XAML to CS:
                            string generatedCode = ConvertingXamlToCSharp.Convert(xaml, sourceFile, fileNameWithPathRelativeToProjectRoot, assemblyNameWithoutExtension, reflectionOnSeparateAppDomain, isFirstPass: !isSecondPass, isSLMigration: isSLMigration, outputRootPath: outputRootPath, outputAppFilesPath: outputAppFilesPath, outputLibrariesPath: outputLibrariesPath, outputResourcesPath: outputResourcesPath, logger: logger);

                            // Add the header that contains the file hash so as to avoid re-processing the file if not needed:
                            generatedCode = CreateHeaderContainingHash(generatedCode, xaml, isSecondPass)
                                            + Environment.NewLine
                                            + Environment.NewLine
                                            + generatedCode;

                            // Create output directory:
                            Directory.CreateDirectory(Path.GetDirectoryName(outputFile));

                            // Save output:
                            using (StreamWriter outfile = new StreamWriter(outputFile))
                            {
                                outfile.Write(generatedCode);
                            }
                        }
                    }

                    //------- DISPLAY THE PROGRESS -------
                    logger.WriteMessage(operationName + " completed in " + executionTimeMeasuring.StopAndGetTimeInSeconds() + " seconds.");

                    return(true);
                }
            }
            catch (Exception ex)
            {
                //-----------------------------------------------------
                // Dispose the static instance of the "ReflectionOnSeparateAppDomainHandler":
                //-----------------------------------------------------

                /*
                 *  We dispose the static instance of the "ReflectionOnSeparateAppDomainHandler"
                 *  that was created in the "BeforeXamlPreprocessor" task, in order to free any
                 *  hooks on the user app DLL's.
                 *  Note: this is normally done in the task named "AfterXamlPreprocessor", but
                 *  since we are going to cancel the Build process, that task will never be
                 *  executed, resulting in potential hooks to the DLL not being freed (causing
                 *  issues when the user recompiles his application). So we free them now.
                 */

                ReflectionOnSeparateAppDomainHandler.Current.Dispose(); // Note: this is not supposed to be null because it was instantiated in the "BeforeXamlPreprocessor" task.

                //-----------------------------------------------------
                // Display the error and cancel the Build process:
                //-----------------------------------------------------
                string message = $"{operationName} failed: {ex.Message}\nNote: the XAML editor sometimes raises errors that are misleading. To see only real non-misleading errors, make sure to close all the XAML editor windows/tabs before compiling.";

                if (ex is wpf::System.Windows.Markup.XamlParseException)
                {
                    int lineNumber = ((wpf::System.Windows.Markup.XamlParseException)ex).LineNumber;
                    logger.WriteError(message, file: sourceFile, lineNumber: lineNumber);
                }
                else
                {
                    logger.WriteError(message, file: sourceFile);
                }

                return(false);
            }
        }
        public static bool Execute(string sourceAssembly, string outputRootPath, string outputResourcesPath, string assembliesToIgnore, string supportedExtensions, ILogger logger, bool isBridgeBasedVersion, string typeForwardingAssemblyPath, string nameOfAssembliesThatDoNotContainUserCode, string coreAssemblyFiles, out List <string> listOfCopiedResXFiles)
        {
            string operationName = "C#/XAML for HTML5: ResourcesExtractorAndCopier";

            try
            {
                using (var executionTimeMeasuring = new ExecutionTimeMeasuring())
                {
                    // Validate input strings:
                    if (string.IsNullOrEmpty(sourceAssembly))
                    {
                        throw new Exception(operationName + " failed because the source assembly argument is invalid.");
                    }

                    //------- DISPLAY THE PROGRESS -------
                    logger.WriteMessage(operationName + " started for assembly \"" + sourceAssembly + "\".");

                    // Determine the absolute output path:
                    string outputPathAbsolute = PathsHelper.GetOutputPathAbsolute(sourceAssembly, outputRootPath);

                    // Create a separate AppDomain so that the types loaded for reflection can be unloaded when done.
                    bool isSuccess = false;
                    using (var reflectionOnSeparateAppDomain = new ReflectionOnSeparateAppDomainHandler(typeForwardingAssemblyPath))
                    {
#if BRIDGE || CSHTML5BLAZOR
                        // Load for the core assemblies first:
                        string[] coreAssemblyFilesArray = coreAssemblyFiles.Split(new char[] { '|' }, StringSplitOptions.RemoveEmptyEntries);
                        foreach (string coreAssemblyFile in coreAssemblyFilesArray)
                        {
                            reflectionOnSeparateAppDomain.LoadAssembly(coreAssemblyFile, loadReferencedAssembliesToo: false, isBridgeBasedVersion: isBridgeBasedVersion, isCoreAssembly: true, nameOfAssembliesThatDoNotContainUserCode: nameOfAssembliesThatDoNotContainUserCode, skipReadingAttributesFromAssemblies: true);
                        }
#endif

                        // Prepare some dictionaries:
                        string[] arrayWithSimpleNameOfAssembliesToIgnore = assembliesToIgnore != null?assembliesToIgnore.Split(new char[] { '|' }, StringSplitOptions.RemoveEmptyEntries) : new string[]
                        {
                        };
                        HashSet <string> simpleNameOfAssembliesToIgnore = new HashSet <string>(arrayWithSimpleNameOfAssembliesToIgnore);
                        string[]         arrayOfSupportedExtensions     = supportedExtensions != null?supportedExtensions.ToLowerInvariant().Split(new char[]
                        {
                            '|'
                        }, StringSplitOptions.RemoveEmptyEntries) : new string[] { };
                        HashSet <string> supportedExtensionsLowercase = new HashSet <string>(arrayOfSupportedExtensions);

                        // Do the extraction and copy:
                        isSuccess = ExtractingAndCopyingResources.Start(
                            sourceAssembly,
                            outputPathAbsolute,
                            outputResourcesPath,
                            simpleNameOfAssembliesToIgnore,
                            supportedExtensionsLowercase,
                            logger,
                            reflectionOnSeparateAppDomain,
                            isBridgeBasedVersion,
                            nameOfAssembliesThatDoNotContainUserCode,
                            out listOfCopiedResXFiles);
                    }

                    //------- DISPLAY THE PROGRESS -------
                    logger.WriteMessage(operationName + (isSuccess ? " completed in " + executionTimeMeasuring.StopAndGetTimeInSeconds() + " seconds." : " failed."));

                    return(isSuccess);
                }
            }
            catch (Exception ex)
            {
                logger.WriteError(operationName + " failed: " + ex.ToString());
                listOfCopiedResXFiles = null;
                return(false);
            }
        }