Inheritance: BaseProfile
Beispiel #1
0
        static Configuration ParseCommandLine(
            IEnumerable<string> arguments, List<BuildGroup> buildGroups, 
            Dictionary<string, IProfile> profiles
        )
        {
            var baseConfig = new Configuration();
            var commandLineConfig = new Configuration();
            IProfile defaultProfile = new Profiles.Default();
            var profileAssemblies = new List<string>();
            bool[] autoloadProfiles = new bool[] { true };
            string[] newDefaultProfile = new string[] { null };
            List<string> filenames;

            {
                var os = new Mono.Options.OptionSet {
                    {"o=|out=",
                        "Specifies the output directory for generated javascript and manifests.",
                        (path) => commandLineConfig.OutputDirectory = Path.GetFullPath(path) },
                    {"nac|noautoconfig",
                        "Suppresses automatic loading of same-named .jsilconfig files located next to solutions and/or assemblies.",
                        (b) => commandLineConfig.AutoLoadConfigFiles = b == null },
                    {"nt|nothreads",
                        "Suppresses use of multiple threads to speed up the translation process.",
                        (b) => commandLineConfig.UseThreads = b == null },
                    {"sbc|suppressbugcheck",
                        "Suppresses JSIL bug checks that detect bugs in .NET runtimes and standard libraries.",
                        (b) => commandLineConfig.RunBugChecks = b == null },

                    "Solution Builder options",
                    {"configuration=",
                        "When building one or more solution files, specifies the build configuration to use (like 'Debug').",
                        (v) => commandLineConfig.SolutionBuilder.Configuration = v },
                    {"platform=",
                        "When building one or more solution files, specifies the build platform to use (like 'x86').",
                        (v) => commandLineConfig.SolutionBuilder.Platform = v },
                    {"target=",
                        "When building one or more solution files, specifies the build target to use (like 'Build'). The default is 'Build'.",
                        (v) => commandLineConfig.SolutionBuilder.Target = v },
                    {"logVerbosity=",
                        "When building one or more solution files, specifies the level of log verbosity. Valid options are 'Quiet', 'Minimal', 'Normal', 'Detailed', and 'Diagnostic'.",
                        (v) => commandLineConfig.SolutionBuilder.LogVerbosity = v },

                    "Assembly options",
                    {"p=|proxy=",
                        "Loads a type proxy assembly to provide type information for the translator.",
                        (name) => commandLineConfig.Assemblies.Proxies.Add(Path.GetFullPath(name)) },
                    {"i=|ignore=",
                        "Specifies a regular expression pattern for assembly names that should be ignored during the translation process.",
                        (regex) => commandLineConfig.Assemblies.Ignored.Add(regex) },
                    {"s=|stub=",
                        "Specifies a regular expression pattern for assembly names that should be stubbed during the translation process. " +
                        "Stubbing forces all methods to be externals.",
                        (regex) => commandLineConfig.Assemblies.Stubbed.Add(regex) },
                    {"nd|nodeps",
                        "Suppresses the automatic loading and translation of assembly dependencies.",
                        (b) => commandLineConfig.IncludeDependencies = b == null},
                    {"nodefaults",
                        "Suppresses the default list of stubbed assemblies.",
                        (b) => commandLineConfig.ApplyDefaults = b == null},
                    {"nolocal",
                        "Disables using local proxy types from translated assemblies.",
                        (b) => commandLineConfig.UseLocalProxies = b == null},
                    {"fv=|frameworkVersion=",
                        "Specifies the version of the .NET framework proxies to use. " +
                        "This ensures that correct type information is provided (as different versions of the framework use different standard libraries). " +
                        "The only accepted value is currently '4.0'. Default: '4.0'",
                        (fv) => commandLineConfig.FrameworkVersion = double.Parse(fv)},

                    "Profile options",
                    {"nap|noautoloadprofiles",
                        "Disables automatic loading of profile assemblies from the compiler directory.",
                        (b) => autoloadProfiles[0] = (b == null)},
                    {"pa=|profileAssembly=",
                        "Loads one or more project profiles from the specified profile assembly. Note that this does not force the profiles to be used.",
                        (filename) => profileAssemblies.Add(filename)},
                    {"dp=|defaultProfile=",
                        "Overrides the default profile to use for projects by specifying the name of the new default profile.",
                        (profileName) => newDefaultProfile[0] = profileName},

                    "CodeGenerator options",
                    {"os",
                        "Suppresses struct copy elimination.",
                        (b) => commandLineConfig.CodeGenerator.EliminateStructCopies = b == null},
                    {"ot",
                        "Suppresses temporary local variable elimination.",
                        (b) => commandLineConfig.CodeGenerator.EliminateTemporaries = b == null},
                    {"oo",
                        "Suppresses simplification of operator expressions and special method calls.",
                        (b) => commandLineConfig.CodeGenerator.SimplifyOperators = b == null},
                    {"ol",
                        "Suppresses simplification of loop blocks.",
                        (b) => commandLineConfig.CodeGenerator.SimplifyLoops = b == null},
                };

                filenames = os.Parse(arguments);

                if (filenames.Count == 0) {
                    var asmName = Assembly.GetExecutingAssembly().GetName();
                    Console.WriteLine("==== JSILc v{0}.{1}.{2} ====", asmName.Version.Major, asmName.Version.Minor, asmName.Version.Revision);
                    Console.WriteLine("Specify one or more compiled assemblies (dll/exe) to translate them. Symbols will be loaded if they exist in the same directory.");
                    Console.WriteLine("You can also specify Visual Studio solution files (sln) to build them and automatically translate their output(s).");
                    Console.WriteLine("Specify the path of a .jsilconfig file to load settings from it.");

                    os.WriteOptionDescriptions(Console.Out);

                    return null;
                }
            }

            {
                if (autoloadProfiles[0])
                    profileAssemblies.AddRange(Directory.GetFiles(
                        GetJSILDirectory(),
                        "JSIL.Profiles.*.dll"
                    ));

                foreach (var filename in profileAssemblies) {
                    var fullPath = Path.GetFullPath(filename);

                    try {
                        var assembly = Assembly.LoadFile(fullPath);

                        foreach (var type in assembly.GetTypes()) {
                            if (
                                type.FindInterfaces(
                                    (interfaceType, o) => interfaceType == (Type)o, typeof(IProfile)
                                ).Length != 1
                            )
                                continue;

                            var ctor = type.GetConstructor(
                                BindingFlags.Public | BindingFlags.Instance,
                                null, System.Type.EmptyTypes, null
                            );
                            var profileInstance = (IProfile)ctor.Invoke(new object[0]);

                            profiles.Add(type.Name, profileInstance);
                        }
                    } catch (Exception exc) {
                        Console.Error.WriteLine("Warning: Failed to load profile '{0}': {1}", filename, exc);
                    }
                }
            }

            commandLineConfig = MergeConfigurations(
                commandLineConfig,
                (from fn in filenames
                 where Path.GetExtension(fn) == ".jsilconfig"
                 select LoadConfiguration(fn)).ToArray()
            );

            if (commandLineConfig.ApplyDefaults.GetValueOrDefault(true)) {
                baseConfig = MergeConfigurations(
                    LoadConfiguration(Path.Combine(
                        GetJSILDirectory(),
                        "defaults.jsilconfig"
                    )),
                    baseConfig
                );
            }

            foreach (var solution in
                     (from fn in filenames where Path.GetExtension(fn) == ".sln" select fn)
                    ) {

                var solutionFullPath = Path.GetFullPath(solution);
                var solutionDir = Path.GetDirectoryName(solutionFullPath);

                if ((solutionDir == null) || (solutionFullPath == null)) {
                    Console.Error.WriteLine("// Can't process solution '{0}' - path seems malformed", solution);
                    continue;
                }

                var solutionConfigPath = Path.Combine(
                    solutionDir,
                    String.Format("{0}.jsilconfig", Path.GetFileName(solutionFullPath))
                );
                var solutionConfig = File.Exists(solutionConfigPath)
                    ? new Configuration[] { LoadConfiguration(solutionConfigPath) }
                    : new Configuration[] {  };

                var mergedSolutionConfig = MergeConfigurations(baseConfig, solutionConfig);
                var config = MergeConfigurations(mergedSolutionConfig, commandLineConfig);
                var buildStarted = DateTime.UtcNow.Ticks;

                var buildResult = SolutionBuilder.SolutionBuilder.Build(
                    solutionFullPath,
                    config.SolutionBuilder.Configuration,
                    config.SolutionBuilder.Platform,
                    config.SolutionBuilder.Target ?? "Build",
                    config.SolutionBuilder.LogVerbosity
                );

                var jss = new JavaScriptSerializer();
                jss.MaxJsonLength = (1024 * 1024) * 64;
                var buildResultJson = jss.Serialize(buildResult);
                buildResult = jss.Deserialize<SolutionBuilder.BuildResult>(buildResultJson);

                var buildEnded = DateTime.UtcNow.Ticks;

                IProfile profile = defaultProfile;

                foreach (var candidateProfile in profiles.Values) {
                    if (!candidateProfile.IsAppropriateForSolution(buildResult))
                        continue;

                    Console.Error.WriteLine("// Auto-selected the profile '{0}' for this project.", candidateProfile.GetType().Name);
                    profile = candidateProfile;
                    break;
                }

                var localVariables = config.ApplyTo(new VariableSet());
                localVariables["SolutionDirectory"] = () => solutionDir;

                var processStarted = DateTime.UtcNow.Ticks;
                profile.ProcessBuildResult(
                    localVariables,
                    profile.GetConfiguration(config),
                    buildResult
                );
                var processEnded = DateTime.UtcNow.Ticks;

                {
                    var logPath = localVariables.ExpandPath(String.Format(
                        "%outputdirectory%/{0}.buildlog", Path.GetFileName(solution)
                    ), false);

                    if (!Directory.Exists(Path.GetDirectoryName(logPath)))
                        Directory.CreateDirectory(Path.GetDirectoryName(logPath));

                    using (var logWriter = new StreamWriter(logPath, false, Encoding.UTF8)) {
                        logWriter.WriteLine(
                            "Build of solution '{0}' processed {1} task(s) and produced {2} result file(s):",
                            solution, buildResult.AllItemsBuilt.Length, buildResult.OutputFiles.Length
                        );

                        foreach (var of in buildResult.OutputFiles)
                            logWriter.WriteLine(of);

                        logWriter.WriteLine("----");
                        logWriter.WriteLine("Elapsed build time: {0:0000.0} second(s).", TimeSpan.FromTicks(buildEnded - buildStarted).TotalSeconds);
                        logWriter.WriteLine("Selected profile '{0}' to process results of this build.", profile.GetType().Name);
                        logWriter.WriteLine("Elapsed processing time: {0:0000.0} second(s).", TimeSpan.FromTicks(processEnded - processStarted).TotalSeconds);
                    }
                }

                var outputFiles = buildResult.OutputFiles.Concat(
                    (from eo in config.SolutionBuilder.ExtraOutputs
                     let expanded = localVariables.ExpandPath(eo, true)
                     select expanded)
                ).ToArray();

                if (outputFiles.Length > 0) {
                    buildGroups.Add(new BuildGroup {
                        BaseConfiguration = mergedSolutionConfig,
                        BaseVariables = localVariables,
                        FilesToBuild = outputFiles,
                        Profile = profile,
                    });
                }
            }

            var assemblyNames = (from fn in filenames
                                 where Path.GetExtension(fn).Contains(",") ||
                                    Path.GetExtension(fn).Contains(" ") ||
                                    Path.GetExtension(fn).Contains("=")
                                 select fn).ToArray();

            var resolver = new Mono.Cecil.DefaultAssemblyResolver();
            var metaResolver = new CachingMetadataResolver(resolver);
            var resolverParameters = new ReaderParameters {
                AssemblyResolver = resolver,
                MetadataResolver = metaResolver,
                ReadSymbols = false,
                ReadingMode = ReadingMode.Deferred,
            };
            var resolvedAssemblyPaths = (from an in assemblyNames
                                      let asm = resolver.Resolve(an, resolverParameters)
                                      where asm != null
                                      select asm.MainModule.FullyQualifiedName).ToArray();

            var mainGroup = (from fn in filenames
                             where
                                 (new[] { ".exe", ".dll" }.Contains(Path.GetExtension(fn)))
                             select fn)
                             .Concat(resolvedAssemblyPaths)
                             .ToArray();

            if (mainGroup.Length > 0) {
                var variables = commandLineConfig.ApplyTo(new VariableSet());

                buildGroups.Add(new BuildGroup {
                    BaseConfiguration = baseConfig,
                    BaseVariables = variables,
                    FilesToBuild = mainGroup,
                    Profile = defaultProfile
                });
            }

            return commandLineConfig;
        }
Beispiel #2
0
        static void ParseCommandLine(IEnumerable <string> arguments, List <BuildGroup> buildGroups, Dictionary <string, IProfile> profiles)
        {
            var      baseConfig        = new Configuration();
            IProfile defaultProfile    = new Profiles.Default();
            var      profileAssemblies = new List <string>();

            bool[]        autoloadProfiles  = new bool[] { true };
            string[]      newDefaultProfile = new string[] { null };
            List <string> filenames;

            {
                var os = new Mono.Options.OptionSet {
                    { "o=|out=",
                      "Specifies the output directory for generated javascript and manifests. " +
                      "You can use '%configpath%' in jsilconfig files to refer to the directory containing the configuration file, and '%assemblypath%' to refer to the directory containing the assembly being translated.",
                      (path) => baseConfig.OutputDirectory = Path.GetFullPath(path) },
                    { "nac|noautoconfig",
                      "Suppresses automatic loading of same-named .jsilconfig files located next to solutions and/or assemblies.",
                      (b) => baseConfig.AutoLoadConfigFiles = b == null },
                    { "nt|nothreads",
                      "Suppresses use of multiple threads to speed up the translation process.",
                      (b) => baseConfig.UseThreads = b == null },

                    "Solution Builder options",
                    { "configuration=",
                      "When building one or more solution files, specifies the build configuration to use (like 'Debug').",
                      (v) => baseConfig.SolutionBuilder.Configuration = v },
                    { "platform=",
                      "When building one or more solution files, specifies the build platform to use (like 'x86').",
                      (v) => baseConfig.SolutionBuilder.Platform = v },
                    { "target=",
                      "When building one or more solution files, specifies the build target to use (like 'Build'). The default is 'Build'.",
                      (v) => baseConfig.SolutionBuilder.Target = v },
                    { "logVerbosity=",
                      "When building one or more solution files, specifies the level of log verbosity. Valid options are 'Quiet', 'Minimal', 'Normal', 'Detailed', and 'Diagnostic'.",
                      (v) => baseConfig.SolutionBuilder.LogVerbosity = v },

                    "Assembly options",
                    { "p=|proxy=",
                      "Loads a type proxy assembly to provide type information for the translator.",
                      (name) => baseConfig.Assemblies.Proxies.Add(Path.GetFullPath(name)) },
                    { "i=|ignore=",
                      "Specifies a regular expression pattern for assembly names that should be ignored during the translation process.",
                      (regex) => baseConfig.Assemblies.Ignored.Add(regex) },
                    { "s=|stub=",
                      "Specifies a regular expression pattern for assembly names that should be stubbed during the translation process. " +
                      "Stubbing forces all methods to be externals.",
                      (regex) => baseConfig.Assemblies.Stubbed.Add(regex) },
                    { "nd|nodeps",
                      "Suppresses the automatic loading and translation of assembly dependencies.",
                      (b) => baseConfig.IncludeDependencies = b == null },
                    { "nodefaults",
                      "Suppresses the default list of stubbed assemblies.",
                      (b) => baseConfig.ApplyDefaults = b == null },
                    { "nolocal",
                      "Disables using local proxy types from translated assemblies.",
                      (b) => baseConfig.UseLocalProxies = b == null },
                    { "fv=|frameworkVersion=",
                      "Specifies the version of the .NET framework proxies to use. " +
                      "This ensures that correct type information is provided (as 3.5 and 4.0 use different standard libraries). " +
                      "Accepted values are '3.5' and '4.0'. Default: '4.0'",
                      (fv) => baseConfig.FrameworkVersion = double.Parse(fv) },

                    "Profile options",
                    { "nap|noautoloadprofiles",
                      "Disables automatic loading of profile assemblies from the compiler directory.",
                      (b) => autoloadProfiles[0] = (b == null) },
                    { "pa=|profileAssembly=",
                      "Loads one or more project profiles from the specified profile assembly. Note that this does not force the profiles to be used.",
                      (filename) => profileAssemblies.Add(filename) },
                    { "dp=|defaultProfile=",
                      "Overrides the default profile to use for projects by specifying the name of the new default profile..",
                      (profileName) => newDefaultProfile[0] = profileName },

                    "Optimizer options",
                    { "os",
                      "Suppresses struct copy elimination.",
                      (b) => baseConfig.Optimizer.EliminateStructCopies = b == null },
                    { "ot",
                      "Suppresses temporary local variable elimination.",
                      (b) => baseConfig.Optimizer.EliminateTemporaries = b == null },
                    { "oo",
                      "Suppresses simplification of operator expressions and special method calls.",
                      (b) => baseConfig.Optimizer.SimplifyOperators = b == null },
                    { "ol",
                      "Suppresses simplification of loop blocks.",
                      (b) => baseConfig.Optimizer.SimplifyLoops = b == null },
                };

                filenames = os.Parse(arguments);

                if (filenames.Count == 0)
                {
                    var asmName = Assembly.GetExecutingAssembly().GetName();
                    Console.WriteLine("==== JSILc v{0}.{1}.{2} ====", asmName.Version.Major, asmName.Version.Minor, asmName.Version.Revision);
                    Console.WriteLine("Specify one or more compiled assemblies (dll/exe) to translate them. Symbols will be loaded if they exist in the same directory.");
                    Console.WriteLine("You can also specify Visual Studio solution files (sln) to build them and automatically translate their output(s).");
                    Console.WriteLine("Specify the path of a .jsilconfig file to load settings from it.");

                    os.WriteOptionDescriptions(Console.Out);

                    return;
                }
            }

            {
                if (autoloadProfiles[0])
                {
                    profileAssemblies.AddRange(Directory.GetFiles(".", "JSIL.Profiles.*.dll"));
                }

                foreach (var filename in profileAssemblies)
                {
                    var fullPath = Path.GetFullPath(filename);

                    try {
                        var assembly = Assembly.LoadFile(fullPath);

                        foreach (var type in assembly.GetTypes())
                        {
                            if (
                                type.FindInterfaces(
                                    (interfaceType, o) => interfaceType == (Type)o, typeof(IProfile)
                                    ).Length != 1
                                )
                            {
                                continue;
                            }

                            var ctor = type.GetConstructor(
                                BindingFlags.Public | BindingFlags.Instance,
                                null, System.Type.EmptyTypes, null
                                );
                            var profileInstance = (IProfile)ctor.Invoke(new object[0]);

                            profiles.Add(type.Name, profileInstance);
                        }
                    } catch (Exception exc) {
                        Console.Error.WriteLine("Warning: Failed to load profile '{0}': {1}", filename, exc);
                    }
                }
            }

            baseConfig = MergeConfigurations(
                baseConfig,
                (from fn in filenames
                 where Path.GetExtension(fn) == ".jsilconfig"
                 select LoadConfiguration(fn)).ToArray()
                );

            foreach (var solution in
                     (from fn in filenames where Path.GetExtension(fn) == ".sln" select fn)
                     )
            {
                var solutionConfigPath = Path.Combine(
                    Path.GetDirectoryName(solution),
                    String.Format("{0}.jsilconfig", Path.GetFileName(solution))
                    );
                var solutionConfig = File.Exists(solutionConfigPath)
                    ? new Configuration[] { LoadConfiguration(solutionConfigPath) }
                    : new Configuration[] { };

                var config      = MergeConfigurations(baseConfig, solutionConfig);
                var buildResult = SolutionBuilder.Build(
                    solution,
                    config.SolutionBuilder.Configuration,
                    config.SolutionBuilder.Platform,
                    config.SolutionBuilder.Target ?? "Build"
                    );

                IProfile profile = defaultProfile;

                foreach (var candidateProfile in profiles.Values)
                {
                    if (!candidateProfile.IsAppropriateForSolution(buildResult))
                    {
                        continue;
                    }

                    Console.Error.WriteLine("// Auto-selected the profile '{0}' for this project.", candidateProfile.GetType().Name);
                    profile = candidateProfile;
                    break;
                }

                profile.ProcessBuildResult(
                    profile.GetConfiguration(config),
                    buildResult
                    );

                if (buildResult.OutputFiles.Length > 0)
                {
                    buildGroups.Add(new BuildGroup {
                        BaseConfiguration = config,
                        FilesToBuild      = buildResult.OutputFiles,
                        Profile           = profile
                    });
                }
            }

            var assemblyNames = (from fn in filenames
                                 where Path.GetExtension(fn).Contains(",") ||
                                 Path.GetExtension(fn).Contains(" ") ||
                                 Path.GetExtension(fn).Contains("=")
                                 select fn).ToArray();

            var resolver           = new Mono.Cecil.DefaultAssemblyResolver();
            var metaResolver       = new CachingMetadataResolver(resolver);
            var resolverParameters = new ReaderParameters {
                AssemblyResolver = resolver,
                MetadataResolver = metaResolver,
                ReadSymbols      = false,
                ReadingMode      = ReadingMode.Deferred,
            };
            var resolvedAssemblyPaths = (from an in assemblyNames
                                         let asm = resolver.Resolve(an, resolverParameters)
                                                   where asm != null
                                                   select asm.MainModule.FullyQualifiedName).ToArray();

            var mainGroup = (from fn in filenames
                             where
                             (new[] { ".exe", ".dll" }.Contains(Path.GetExtension(fn)))
                             select fn)
                            .Concat(resolvedAssemblyPaths)
                            .ToArray();

            if (mainGroup.Length > 0)
            {
                buildGroups.Add(new BuildGroup {
                    BaseConfiguration = baseConfig,
                    FilesToBuild      = mainGroup,
                    Profile           = defaultProfile
                });
            }
        }
Beispiel #3
0
        static Configuration ParseCommandLine(
            IEnumerable <string> arguments, List <BuildGroup> buildGroups,
            Dictionary <string, IProfile> profiles, Dictionary <string, IAnalyzer> analyzers,
            AssemblyCache assemblyCache
            )
        {
            var      baseConfig         = new Configuration();
            var      commandLineConfig  = new Configuration();
            IProfile defaultProfile     = new Profiles.Default();
            var      profileAssemblies  = new List <string>();
            var      analyzerAssemblies = new List <string>();

            bool[]        autoloadProfiles  = new bool[] { true };
            bool[]        autoloadAnalyzers = new bool[] { true };
            string[]      newDefaultProfile = new string[] { null };
            List <string> filenames;

            {
                var os = new Mono.Options.OptionSet {
                    { "o=|out=",
                      "Specifies the output directory for generated javascript and manifests.",
                      (path) => commandLineConfig.OutputDirectory = Path.GetFullPath(path) },
                    { "nac|noautoconfig",
                      "Suppresses automatic loading of same-named .jsilconfig files located next to solutions and/or assemblies.",
                      (b) => commandLineConfig.AutoLoadConfigFiles = b == null },
                    { "nt|nothreads",
                      "Suppresses use of multiple threads to speed up the translation process.",
                      (b) => commandLineConfig.UseThreads = b == null },
                    { "sbc|suppressbugcheck",
                      "Suppresses JSIL bug checks that detect bugs in .NET runtimes and standard libraries.",
                      (b) => commandLineConfig.RunBugChecks = b == null },

                    "Solution Builder options",
                    { "configuration=",
                      "When building one or more solution files, specifies the build configuration to use (like 'Debug').",
                      (v) => commandLineConfig.SolutionBuilder.Configuration = v },
                    { "platform=",
                      "When building one or more solution files, specifies the build platform to use (like 'x86').",
                      (v) => commandLineConfig.SolutionBuilder.Platform = v },
                    { "target=",
                      "When building one or more solution files, specifies the build target to use (like 'Build'). The default is 'Build'.",
                      (v) => commandLineConfig.SolutionBuilder.Target = v },
                    { "logVerbosity=",
                      "When building one or more solution files, specifies the level of log verbosity. Valid options are 'Quiet', 'Minimal', 'Normal', 'Detailed', and 'Diagnostic'.",
                      (v) => commandLineConfig.SolutionBuilder.LogVerbosity = v },

                    "Assembly options",
                    { "p=|proxy=",
                      "Loads a type proxy assembly to provide type information for the translator.",
                      (name) => commandLineConfig.Assemblies.Proxies.Add(Path.GetFullPath(name)) },
                    { "i=|ignore=",
                      "Specifies a regular expression pattern for assembly names that should be ignored during the translation process.",
                      (regex) => commandLineConfig.Assemblies.Ignored.Add(regex) },
                    { "s=|stub=",
                      "Specifies a regular expression pattern for assembly names that should be stubbed during the translation process. " +
                      "Stubbing forces all methods to be externals.",
                      (regex) => commandLineConfig.Assemblies.Stubbed.Add(regex) },
                    { "nd|nodeps",
                      "Suppresses the automatic loading and translation of assembly dependencies.",
                      (b) => commandLineConfig.IncludeDependencies = b == null },
                    { "nodefaults",
                      "Suppresses the default list of stubbed assemblies.",
                      (b) => commandLineConfig.ApplyDefaults = b == null },
                    { "nolocal",
                      "Disables using local proxy types from translated assemblies.",
                      (b) => commandLineConfig.UseLocalProxies = b == null },
                    { "fv=|frameworkVersion=",
                      "Specifies the version of the .NET framework proxies to use. " +
                      "This ensures that correct type information is provided (as different versions of the framework use different standard libraries). " +
                      "The only accepted value is currently '4.0'. Default: '4.0'",
                      (fv) => commandLineConfig.FrameworkVersion = double.Parse(fv) },

                    "Profile options",
                    { "nap|noautoloadprofiles",
                      "Disables automatic loading of profile assemblies from the compiler directory.",
                      (b) => autoloadProfiles[0] = (b == null) },
                    { "pa=|profileAssembly=",
                      "Loads one or more project profiles from the specified profile assembly. Note that this does not force the profiles to be used.",
                      profileAssemblies.Add },
                    { "dp=|defaultProfile=",
                      "Overrides the default profile to use for projects by specifying the name of the new default profile.",
                      (profileName) => newDefaultProfile[0] = profileName },

                    "CodeGenerator options",
                    { "os",
                      "Suppresses struct copy elimination.",
                      (b) => commandLineConfig.CodeGenerator.EliminateStructCopies = b == null },
                    { "ot",
                      "Suppresses temporary local variable elimination.",
                      (b) => commandLineConfig.CodeGenerator.EliminateTemporaries = b == null },
                    { "oo",
                      "Suppresses simplification of operator expressions and special method calls.",
                      (b) => commandLineConfig.CodeGenerator.SimplifyOperators = b == null },
                    { "ol",
                      "Suppresses simplification of loop blocks.",
                      (b) => commandLineConfig.CodeGenerator.SimplifyLoops = b == null },
                };

                filenames = os.Parse(arguments);

                if (filenames.Count == 0)
                {
                    var asmName = Assembly.GetExecutingAssembly().GetName();
                    Console.WriteLine("==== JSILc v{0}.{1}.{2} ====", asmName.Version.Major, asmName.Version.Minor, asmName.Version.Revision);
                    Console.WriteLine("Specify one or more compiled assemblies (dll/exe) to translate them. Symbols will be loaded if they exist in the same directory.");
                    Console.WriteLine("You can also specify Visual Studio solution files (sln) to build them and automatically translate their output(s).");
                    Console.WriteLine("Specify the path of a .jsilconfig file to load settings from it.");

                    os.WriteOptionDescriptions(Console.Out);

                    return(null);
                }
            }

            {
                if (autoloadProfiles[0])
                {
                    profileAssemblies.AddRange(Directory.GetFiles(
                                                   GetJSILDirectory(),
                                                   "JSIL.Profiles.*.dll"
                                                   ));
                }

                if (autoloadAnalyzers[0])
                {
                    analyzerAssemblies.AddRange(Directory.GetFiles(
                                                    GetJSILDirectory(),
                                                    "JSIL.Analysis.*.dll"
                                                    ));
                }

                foreach (var filename in profileAssemblies)
                {
                    var fullPath = Path.GetFullPath(filename);

                    try {
                        IProfile profileInstance = CreateExtensionInstance <IProfile>(fullPath);
                        if (profileInstance != null)
                        {
                            profiles.Add(profileInstance.GetType().Name, profileInstance);
                        }
                    } catch (Exception exc) {
                        Console.Error.WriteLine("Warning: Failed to load profile '{0}': {1}", filename, exc);
                    }
                }

                foreach (var filename in analyzerAssemblies)
                {
                    var fullPath = Path.GetFullPath(filename);

                    try {
                        IAnalyzer analyzerInstance = CreateExtensionInstance <IAnalyzer>(fullPath);
                        if (analyzerInstance != null)
                        {
                            analyzers.Add(analyzerInstance.GetType().Name, analyzerInstance);
                        }
                    } catch (Exception exc) {
                        Console.Error.WriteLine("Warning: Failed to load analyzer '{0}': {1}", filename, exc);
                    }
                }
            }

            var commandLineConfigFilenames =
                (from fn in filenames
                 where Path.GetExtension(fn) == ".jsilconfig"
                 select fn).ToArray();

            // Fail early on nonexistent configuration files
            foreach (var filename in commandLineConfigFilenames)
            {
                if (!File.Exists(filename))
                {
                    throw new FileNotFoundException(filename);
                }
            }

            commandLineConfig = MergeConfigurations(
                commandLineConfig,
                (from fn in commandLineConfigFilenames
                 select LoadConfiguration(fn)).ToArray()
                );

            if (commandLineConfig.ApplyDefaults.GetValueOrDefault(true))
            {
                baseConfig = MergeConfigurations(
                    LoadConfiguration(Path.Combine(
                                          GetJSILDirectory(),
                                          "defaults.jsilconfig"
                                          )),
                    baseConfig
                    );
            }

            foreach (var solution in
                     (from fn in filenames where Path.GetExtension(fn) == ".sln" select fn)
                     )
            {
                var solutionFullPath = Path.GetFullPath(solution);
                var solutionDir      = Path.GetDirectoryName(solutionFullPath);

                if (solutionDir == null)
                {
                    Console.Error.WriteLine("// Can't process solution '{0}' - path seems malformed", solution);
                    continue;
                }

                // Fail early if a solution file is missing
                if (!File.Exists(solutionFullPath))
                {
                    throw new FileNotFoundException(solutionFullPath);
                }

                var solutionConfigPath = Path.Combine(
                    solutionDir,
                    String.Format("{0}.jsilconfig", Path.GetFileName(solutionFullPath))
                    );
                var solutionConfig = File.Exists(solutionConfigPath)
                    ? new Configuration[] { LoadConfiguration(solutionConfigPath) }
                    : new Configuration[] {  };

                var mergedSolutionConfig = MergeConfigurations(baseConfig, solutionConfig);
                var config       = MergeConfigurations(mergedSolutionConfig, commandLineConfig);
                var buildStarted = DateTime.UtcNow.Ticks;

                var buildResult = SolutionBuilder.SolutionBuilder.Build(
                    solutionFullPath,
                    config.SolutionBuilder.Configuration,
                    config.SolutionBuilder.Platform,
                    config.SolutionBuilder.Target ?? "Build",
                    config.SolutionBuilder.LogVerbosity
                    );

                var jss = new JavaScriptSerializer {
                    MaxJsonLength = (1024 * 1024) * 64
                };

                var buildResultJson = jss.Serialize(buildResult);
                buildResult = jss.Deserialize <SolutionBuilder.BuildResult>(buildResultJson);

                var buildEnded = DateTime.UtcNow.Ticks;

                IProfile profile = defaultProfile;

                foreach (var candidateProfile in profiles.Values)
                {
                    if (!candidateProfile.IsAppropriateForSolution(buildResult))
                    {
                        continue;
                    }

                    Console.Error.WriteLine("// Auto-selected the profile '{0}' for this project.", candidateProfile.GetType().Name);
                    profile = candidateProfile;
                    break;
                }

                var localVariables = config.ApplyTo(new VariableSet());
                localVariables["SolutionDirectory"] = () => solutionDir;

                // HACK to let you use assemblyname/etc when copying output files.
                var buildResultAssembly =
                    buildResult.OutputFiles.FirstOrDefault((fn) => Path.GetExtension(fn) == ".exe") ??
                    buildResult.OutputFiles.FirstOrDefault((fn) => Path.GetExtension(fn) == ".dll");

                if (buildResultAssembly != null)
                {
                    localVariables.SetAssemblyPath(buildResultAssembly);
                }

                var processStarted = DateTime.UtcNow.Ticks;
                profile.ProcessBuildResult(
                    localVariables,
                    profile.GetConfiguration(config),
                    buildResult
                    );
                var processEnded = DateTime.UtcNow.Ticks;

                {
                    var logPath = localVariables.ExpandPath(String.Format(
                                                                "%outputdirectory%/{0}.buildlog", Path.GetFileName(solution)
                                                                ), false);

                    if (!Directory.Exists(Path.GetDirectoryName(logPath)))
                    {
                        Directory.CreateDirectory(Path.GetDirectoryName(logPath));
                    }

                    using (var logWriter = new StreamWriter(logPath, false, Encoding.UTF8)) {
                        logWriter.WriteLine(
                            "Build of solution '{0}' processed {1} task(s) and produced {2} result file(s):",
                            solution, buildResult.AllItemsBuilt.Length, buildResult.OutputFiles.Length
                            );

                        foreach (var of in buildResult.OutputFiles)
                        {
                            logWriter.WriteLine(of);
                        }

                        logWriter.WriteLine("----");
                        logWriter.WriteLine("Elapsed build time: {0:0000.0} second(s).", TimeSpan.FromTicks(buildEnded - buildStarted).TotalSeconds);
                        logWriter.WriteLine("Selected profile '{0}' to process results of this build.", profile.GetType().Name);
                        logWriter.WriteLine("Elapsed processing time: {0:0000.0} second(s).", TimeSpan.FromTicks(processEnded - processStarted).TotalSeconds);
                    }
                }

                var outputFiles = buildResult.OutputFiles.Concat(
                    (from eo in config.SolutionBuilder.ExtraOutputs
                     let expanded = localVariables.ExpandPath(eo, true)
                                    select expanded)
                    ).ToArray();

                if (outputFiles.Length > 0)
                {
                    var sa = new HashSet <string>();

                    var group = new BuildGroup {
                        BaseConfiguration = mergedSolutionConfig,
                        BaseVariables     = localVariables,
                        FilesToBuild      = PurgeDuplicateFilesFromBuildGroup(outputFiles, assemblyCache, sa),
                        Profile           = profile,
                    };
                    group.SkippedAssemblies = sa.ToArray();

                    buildGroups.Add(group);
                }
            }

            var assemblyNames = (from fn in filenames
                                 where Path.GetExtension(fn).Contains(",") ||
                                 Path.GetExtension(fn).Contains(" ") ||
                                 Path.GetExtension(fn).Contains("=")
                                 select fn).ToArray();

            var resolver           = new Mono.Cecil.DefaultAssemblyResolver();
            var metaResolver       = new CachingMetadataResolver(resolver);
            var resolverParameters = new ReaderParameters {
                AssemblyResolver = resolver,
                MetadataResolver = metaResolver,
                ReadSymbols      = false,
                ReadingMode      = ReadingMode.Deferred,
            };
            var resolvedAssemblyPaths = (from an in assemblyNames
                                         let asm = resolver.Resolve(an, resolverParameters)
                                                   where asm != null
                                                   select asm.MainModule.FullyQualifiedName).ToArray();

            var mainGroup = (from fn in filenames
                             where
                             (new[] { ".exe", ".dll" }.Contains(Path.GetExtension(fn)))
                             select fn)
                            .Concat(resolvedAssemblyPaths)
                            .ToArray();

            if (mainGroup.Length > 0)
            {
                var variables = commandLineConfig.ApplyTo(new VariableSet());

                // Fail early if any assemblies are missing
                foreach (var filename in mainGroup)
                {
                    if (!File.Exists(filename))
                    {
                        throw new FileNotFoundException(filename);
                    }
                }

                buildGroups.Add(new BuildGroup {
                    BaseConfiguration = baseConfig,
                    BaseVariables     = variables,
                    FilesToBuild      = mainGroup,
                    Profile           = defaultProfile
                });
            }

            return(commandLineConfig);
        }
Beispiel #4
0
        static void ParseCommandLine(IEnumerable<string> arguments, List<BuildGroup> buildGroups, Dictionary<string, IProfile> profiles)
        {
            var baseConfig = new Configuration();
            IProfile defaultProfile = new Profiles.Default();
            var profileAssemblies = new List<string>();
            bool[] autoloadProfiles = new bool[] { true };
            string[] newDefaultProfile = new string[] { null };
            List<string> filenames;

            {
                var os = new Mono.Options.OptionSet {
                    {"o=|out=",
                        "Specifies the output directory for generated javascript and manifests. " +
                        "You can use '%configpath%' in jsilconfig files to refer to the directory containing the configuration file, and '%assemblypath%' to refer to the directory containing the assembly being translated.",
                        (path) => baseConfig.OutputDirectory = Path.GetFullPath(path) },
                    {"nac|noautoconfig",
                        "Suppresses automatic loading of same-named .jsilconfig files located next to solutions and/or assemblies.",
                        (b) => baseConfig.AutoLoadConfigFiles = b == null },
                    {"nt|nothreads",
                        "Suppresses use of multiple threads to speed up the translation process.",
                        (b) => baseConfig.UseThreads = b == null },

                    "Solution Builder options",
                    {"configuration=",
                        "When building one or more solution files, specifies the build configuration to use (like 'Debug').",
                        (v) => baseConfig.SolutionBuilder.Configuration = v },
                    {"platform=",
                        "When building one or more solution files, specifies the build platform to use (like 'x86').",
                        (v) => baseConfig.SolutionBuilder.Platform = v },

                    "Assembly options",
                    {"p=|proxy=",
                        "Loads a type proxy assembly to provide type information for the translator.",
                        (name) => baseConfig.Assemblies.Proxies.Add(Path.GetFullPath(name)) },
                    {"i=|ignore=",
                        "Specifies a regular expression pattern for assembly names that should be ignored during the translation process.",
                        (regex) => baseConfig.Assemblies.Ignored.Add(regex) },
                    {"s=|stub=",
                        "Specifies a regular expression pattern for assembly names that should be stubbed during the translation process. " +
                        "Stubbing forces all methods to be externals.",
                        (regex) => baseConfig.Assemblies.Stubbed.Add(regex) },
                    {"nd|nodeps",
                        "Suppresses the automatic loading and translation of assembly dependencies.",
                        (b) => baseConfig.IncludeDependencies = b == null},
                    {"nodefaults",
                        "Suppresses the default list of stubbed assemblies.",
                        (b) => baseConfig.ApplyDefaults = b == null},
                    {"nolocal",
                        "Disables using local proxy types from translated assemblies.",
                        (b) => baseConfig.UseLocalProxies = b == null},
                    {"fv=|frameworkVersion=",
                        "Specifies the version of the .NET framework proxies to use. " +
                        "This ensures that correct type information is provided (as 3.5 and 4.0 use different standard libraries). " +
                        "Accepted values are '3.5' and '4.0'. Default: '4.0'",
                        (fv) => baseConfig.FrameworkVersion = double.Parse(fv)},

                    "Profile options",
                    {"nap|noautoloadprofiles",
                        "Disables automatic loading of profile assemblies from the compiler directory.",
                        (b) => autoloadProfiles[0] = (b == null)},
                    {"pa=|profileAssembly=",
                        "Loads one or more project profiles from the specified profile assembly. Note that this does not force the profiles to be used.",
                        (filename) => profileAssemblies.Add(filename)},
                    {"dp=|defaultProfile=",
                        "Overrides the default profile to use for projects by specifying the name of the new default profile..",
                        (profileName) => newDefaultProfile[0] = profileName},

                    "Optimizer options",
                    {"os",
                        "Suppresses struct copy elimination.",
                        (b) => baseConfig.Optimizer.EliminateStructCopies = b == null},
                    {"ot",
                        "Suppresses temporary local variable elimination.",
                        (b) => baseConfig.Optimizer.EliminateTemporaries = b == null},
                    {"oo",
                        "Suppresses simplification of operator expressions and special method calls.",
                        (b) => baseConfig.Optimizer.SimplifyOperators = b == null},
                    {"ol",
                        "Suppresses simplification of loop blocks.",
                        (b) => baseConfig.Optimizer.SimplifyLoops = b == null},
                };

                filenames = os.Parse(arguments);

                if (filenames.Count == 0) {
                    var asmName = Assembly.GetExecutingAssembly().GetName();
                    Console.WriteLine("==== JSILc v{0}.{1}.{2} ====", asmName.Version.Major, asmName.Version.Minor, asmName.Version.Revision);
                    Console.WriteLine("Specify one or more compiled assemblies (dll/exe) to translate them. Symbols will be loaded if they exist in the same directory.");
                    Console.WriteLine("You can also specify Visual Studio solution files (sln) to build them and automatically translate their output(s).");
                    Console.WriteLine("Specify the path of a .jsilconfig file to load settings from it.");

                    os.WriteOptionDescriptions(Console.Out);

                    return;
                }
            }

            {
                if (autoloadProfiles[0])
                    profileAssemblies.AddRange(Directory.GetFiles(".", "JSIL.Profiles.*.dll"));

                foreach (var filename in profileAssemblies) {
                    var fullPath = Path.GetFullPath(filename);

                    try {
                        var assembly = Assembly.LoadFile(fullPath);

                        foreach (var type in assembly.GetTypes()) {
                            if (
                                type.FindInterfaces(
                                    (interfaceType, o) => interfaceType == (Type)o, typeof(IProfile)
                                ).Length != 1
                            )
                                continue;

                            var ctor = type.GetConstructor(
                                BindingFlags.Public | BindingFlags.Instance,
                                null, System.Type.EmptyTypes, null
                            );
                            var profileInstance = (IProfile)ctor.Invoke(new object[0]);

                            profiles.Add(type.Name, profileInstance);
                        }
                    } catch (Exception exc) {
                        Console.Error.WriteLine("Warning: Failed to load profile '{0}': {1}", filename, exc);
                    }
                }
            }

            baseConfig = MergeConfigurations(
                baseConfig,
                (from fn in filenames
                 where Path.GetExtension(fn) == ".jsilconfig"
                 select LoadConfiguration(fn)).ToArray()
            );

            foreach (var solution in
                     (from fn in filenames where Path.GetExtension(fn) == ".sln" select fn)
                    ) {

                var solutionConfigPath = Path.Combine(
                    Path.GetDirectoryName(solution),
                    String.Format("{0}.jsilconfig", Path.GetFileName(solution))
                );
                var solutionConfig = File.Exists(solutionConfigPath)
                    ? new Configuration[] { LoadConfiguration(solutionConfigPath) }
                    : new Configuration[] { };

                var config = MergeConfigurations(baseConfig, solutionConfig);
                var buildResult = SolutionBuilder.Build(
                    solution,
                    config.SolutionBuilder.Configuration,
                    config.SolutionBuilder.Platform
                );

                IProfile profile = defaultProfile;

                foreach (var candidateProfile in profiles.Values) {
                    if (!candidateProfile.IsAppropriateForSolution(buildResult))
                        continue;

                    Console.Error.WriteLine("// Auto-selected the profile '{0}' for this project.", candidateProfile.GetType().Name);
                    profile = candidateProfile;
                    break;
                }

                profile.ProcessBuildResult(
                    profile.GetConfiguration(config),
                    buildResult
                );

                buildGroups.Add(new BuildGroup {
                    BaseConfiguration = config,
                    FilesToBuild = buildResult.OutputFiles,
                    Profile = profile
                });
            }

            var mainGroup = (from fn in filenames
                             where
                                 (new[] { ".exe", ".dll" }.Contains(Path.GetExtension(fn)))
                             select fn).ToArray();

            if (mainGroup.Length > 0)
                buildGroups.Add(new BuildGroup {
                    BaseConfiguration = baseConfig,
                    FilesToBuild = mainGroup,
                    Profile = defaultProfile
                });
        }