Example #1
0
        }                                         // This is True if the SGEN command line exceeds 32,000 characters and is therefore split into "SGenCommandLineParameters" and "SGenCommandLineParametersContinued".

        public override bool Execute()
        {
            ILogger logger        = new LoggerThatUsesTaskOutput(this);
            string  operationName = "C#/XAML for HTML5: SerializationAssembliesGenerator";

            try
            {
                // Retrieve the full path of sgen.exe:
                string sgenFullPath;
                if (!GeneratingSerializationAssemblies.TryGetLocationOfSGenExe(out sgenFullPath) ||
                    string.IsNullOrEmpty(sgenFullPath))
                {
                    throw new Exception("Could not find the file sgen.exe: please contact [email protected] or ignore this error by adding the following line to your CSPROJ file: <CSharpXamlForHtml5SkipSerializationAssemblies>True</CSharpXamlForHtml5SkipSerializationAssemblies>");
                }

                // Retrieve its directory:
                var sgenDirectoryLongPath = Path.GetDirectoryName(sgenFullPath);
                if (string.IsNullOrEmpty(sgenDirectoryLongPath))
                {
                    throw new Exception("Unable to generate short 8.3 path from long path: please report this issue to [email protected]");
                }

                //  Convert that directory to the short 8.3-filenames format, so that we don't need to surround the path with double quotes (which don't work with the MSBuild Exec tasc):
                SGenDirectory = ShortPathHelper.GetShortPathName(sgenDirectoryLongPath);

                // Generate command line parameters:
                string sgenCommandLineParameters;
                string sgenCommandLineParametersContinued;
                bool   sgenIsContinued;
                bool   isSuccess = GeneratingSerializationAssemblies.GenerateSgenCommandLineParameters(IntermediateOutputDirectory, SourceAssembly, new LoggerThatUsesTaskOutput(this), IsBridgeBasedVersion, out sgenCommandLineParameters, out sgenCommandLineParametersContinued, out sgenIsContinued);
                SGenCommandLineParameters          = sgenCommandLineParameters;
                SGenCommandLineParametersContinued = sgenCommandLineParametersContinued;
                SGenIsContinued = sgenIsContinued;

                return(isSuccess);
            }
            catch (ReflectionTypeLoadException ex)
            {
                logger.WriteError(operationName + " failed. " + Environment.NewLine + Environment.NewLine + "LOADER EXCEPTIONS:" + Environment.NewLine + Environment.NewLine + ConvertLoaderExceptionsToString(ex) + Environment.NewLine + Environment.NewLine + "GENERAL EXCEPTION:" + Environment.NewLine + Environment.NewLine + ex.ToString());
                return(false);
            }
            catch (Exception ex)
            {
                logger.WriteError(operationName + " failed : " + ex.ToString());
                return(false);
            }
        }
        internal static bool GenerateSgenCommandLineParameters(string intermediateOutputDirectory, string sourceAssembly, ILogger logger, bool isBridgeBasedVersion, out string sgenCommandLineParameters, out string sgenCommandLineParametersContinued, out bool sgenIsContinued)
        {
            //-------------------------------------------------------------------------------
            // Create or clear the obj/Debug/TempSGen folder
            //-------------------------------------------------------------------------------
            string tempFolderForSerializationAssemblies = GetTempFolderForSerializationAssemblies(intermediateOutputDirectory);

            if (Directory.Exists(tempFolderForSerializationAssemblies))
            {
                foreach (string filePath in Directory.GetFiles(tempFolderForSerializationAssemblies))
                {
                    try
                    {
                        File.Delete(filePath);
                    }
                    catch (Exception ex)
                    {
                        logger.WriteError("Could not delete the following temporary file: " + filePath + "   - Please delete the file manually. If the error persists, please contact [email protected] - " + ex.Message);
                        sgenCommandLineParameters          = null;
                        sgenCommandLineParametersContinued = null;
                        sgenIsContinued = false;
                        return(false);
                    }
                }
            }
            else
            {
                Directory.CreateDirectory(tempFolderForSerializationAssemblies);
            }

            //-------------------------------------------------------------------------------
            // Find all the types for which we need to create the serialization assemblies:
            //-------------------------------------------------------------------------------

            string[] typesThatAreSerializable;

            // Create the "TypesResolver" on a separate AppDomain so that the types loaded for reflection can be unloaded when done.
            using (var reflectionOnSeparateAppDomain = new ReflectionOnSeparateAppDomainHandler())
            {
                string assemblySimpleName = reflectionOnSeparateAppDomain.LoadAssembly(sourceAssembly, loadReferencedAssembliesToo: false, isBridgeBasedVersion: isBridgeBasedVersion, isCoreAssembly: false, nameOfAssembliesThatDoNotContainUserCode: "");
                string commaSeparatedTypesThatAreSerializable = reflectionOnSeparateAppDomain.FindCommaSeparatedTypesThatAreSerializable(assemblySimpleName);
                typesThatAreSerializable = commaSeparatedTypesThatAreSerializable.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
            }

            //-------------------------------------------------------------------------------
            // Generate the command line call to the "sgen.exe" tool, that will generate the C# source code for the serialization assemblies:
            //-------------------------------------------------------------------------------

            if (typesThatAreSerializable.Length > 0)
            {
                StringBuilder commandLineBuilder          = new StringBuilder();
                StringBuilder commandLineBuilderContinued = new StringBuilder();

                string sourceAssemblyAbsolutePath = Path.Combine(Directory.GetCurrentDirectory(), sourceAssembly);

                string shortPathName = ShortPathHelper.GetShortPathName(sourceAssemblyAbsolutePath); // Note: we call "ShortPathHelper.GetShortPathName" so that we don't need to surround the path with double quotes (which don't work with the MSBuild Exec tasc):

                commandLineBuilder.Append(shortPathName);
                commandLineBuilderContinued.Append(shortPathName);

                commandLineBuilder.Append(" /keep"); // Note: "keep" will prevent sgen from deleting the ".cs" source file after generating the serialization assembly
                commandLineBuilderContinued.Append(" /keep");

                commandLineBuilder.Append(" /v"); // Note: "v" will display verbose output for debugging, and will list types from the target assembly that cannot be serialized with the XmlSerializer.
                commandLineBuilderContinued.Append(" /v");

                commandLineBuilder.Append(" /force"); // Note: "force" will force replace the generated assembly if it already exists.
                commandLineBuilderContinued.Append(" /force");

                int charactersCount = 0; // We count the characters because command lines are limited to 32,768 characters.
                sgenIsContinued = false;
                Dictionary <string, string> typesLocalNameToFullName = new Dictionary <string, string>();
                foreach (string serializableType in typesThatAreSerializable)
                {
                    // Verify that there are no 2 classes with the same local name (SGEN.exe does not support processing two classes with the same local name, unless they have some XML attribute to specify the namespace, but the current version of the XmlSerializer does not support such XML namespace attributes:
                    string serializableTypeLocalName = (serializableType.Contains('.') ? serializableType.Substring(serializableType.LastIndexOf('.') + 1) : serializableType);
                    if (typesLocalNameToFullName.ContainsKey(serializableTypeLocalName))
                    {
                        throw new Exception(@"The following serializable classes have the same name: """ + serializableType + @""" and """ + typesLocalNameToFullName[serializableTypeLocalName] + @""". The current version of C#/XAML for HTML5 does not allow serializing two classes that have the same name. This is due to the fact that the XmlSerializer does not support namespaces at the moment. To work around this limitation, please rename one of the two classes, or remove its [Serializable] or [DataContract] attribute.");
                    }
                    else
                    {
                        typesLocalNameToFullName.Add(serializableTypeLocalName, serializableType);
                    }

                    // Build the command line parameter related to the list of types that are serializable:
                    string fragment = " /type:" + serializableType; // Note: the full type name (ie. namespace + name) is required.

                    if (!sgenIsContinued)                           // This is due to the fact that command lines are limited to 32,768 characters, so we split into two calls if necessary.
                    {
                        commandLineBuilder.Append(fragment);
                    }
                    else
                    {
                        commandLineBuilderContinued.Append(fragment);
                    }

                    charactersCount += fragment.Length;
                    if (charactersCount > 32000)
                    {
                        sgenIsContinued = true;
                    }
                    if (charactersCount > 64000)
                    {
                        throw new Exception("The maximum length of the SGEN command line has been exceeded (64,000 characters). Please reduce the number of serializable types and try again.");
                    }
                }

                string outParam = @" /out:" + ShortPathHelper.GetShortPathName(tempFolderForSerializationAssemblies); // Note: we call "ShortPathHelper.GetShortPathName" so that we don't need to surround the path with double quotes (which don't work with the MSBuild Exec tasc):
                commandLineBuilder.Append(outParam);
                commandLineBuilderContinued.Append(outParam);


                sgenCommandLineParameters = commandLineBuilder.ToString();

                if (sgenIsContinued)
                {
                    sgenCommandLineParametersContinued = commandLineBuilderContinued.ToString();
                }
                else
                {
                    sgenCommandLineParametersContinued = string.Empty;
                }


                // Fix the 8192 characters length limitation (for more information, read the comments in the method "Fix8192charactersIssue"):
                sgenCommandLineParameters          = Fix8192charactersIssue(sgenCommandLineParameters);
                sgenCommandLineParametersContinued = Fix8192charactersIssue(sgenCommandLineParametersContinued);
            }
            else
            {
                sgenCommandLineParameters          = string.Empty;
                sgenCommandLineParametersContinued = string.Empty;
                sgenIsContinued = false;
            }

            logger.WriteMessage("SGEN command line parameters: " + sgenCommandLineParameters, Microsoft.Build.Framework.MessageImportance.Low);
            return(true);
        }