Ejemplo n.º 1
0
        public static BuildContext Parse(
            string projectDir,
            string buildFile,
            string nullableTargetName,
            IList <Wax.BuildArg> buildArgOverrides,
            IList <Wax.ExtensionArg> extensionArgOverrides)
        {
            BuildRoot buildInput = GetBuildRoot(buildFile, projectDir);

            Dictionary <string, Target> targetsByName = new Dictionary <string, Target>();

            foreach (Target target in buildInput.Targets)
            {
                string name = target.Name;
                if (name == null)
                {
                    throw new InvalidOperationException("A target in the build file is missing a name.");
                }
                if (targetsByName.ContainsKey(name))
                {
                    throw new InvalidOperationException("There are multiple build targets with the name '" + name + "'.");
                }
                targetsByName[name] = target;
            }

            List <Wax.BuildArg>     buildArgs     = new List <Wax.BuildArg>();
            List <Wax.ExtensionArg> extensionArgs = new List <Wax.ExtensionArg>();
            List <BuildVar>         buildVars     = new List <BuildVar>();

            List <BuildItem> buildItems = new List <BuildItem>();

            if (nullableTargetName == null)
            {
                buildItems.Add(buildInput);
            }
            else
            {
                HashSet <string> targetsVisited = new HashSet <string>();
                string           nextItem       = nullableTargetName;
                Target           walker;
                while (nextItem != null)
                {
                    if (targetsVisited.Contains(nextItem))
                    {
                        throw new InvalidOperationException("Build target inheritance loop for '" + nextItem + "' contains a cycle.");
                    }
                    targetsVisited.Add(nextItem);
                    if (!targetsByName.ContainsKey(nextItem))
                    {
                        throw new InvalidOperationException("There is no build target named: '" + nextItem + "'.");
                    }
                    walker = targetsByName[nextItem];
                    buildItems.Add(walker);
                    nextItem = walker.InheritFrom;
                }
                buildItems.Add(buildInput);
            }

            buildItems.Reverse(); // The attributes in the leaf node have precedence.

            foreach (BuildItem currentItem in buildItems)
            {
                buildArgs.AddRange(currentItem.BuildArgs);
                extensionArgs.AddRange(currentItem.ExtensionArgs);
                buildVars.AddRange(currentItem.BuildVars);
            }

            if (buildArgOverrides != null)
            {
                buildArgs.AddRange(buildArgOverrides);
            }
            if (extensionArgOverrides != null)
            {
                extensionArgs.AddRange(extensionArgOverrides);
            }

            PercentReplacer pr = new PercentReplacer()
                                 .AddReplacement("TARGET_NAME", nullableTargetName ?? "");

            // Do a first pass over all the build args to fetch anything that is used by %PERCENT_REPLACEMENT% and anything
            // that can be defined with multiple values in a list.
            List <string>       sources             = new List <string>();
            List <string>       icons               = new List <string>();
            string              version             = "1.0";
            Locale              compilerLocale      = Locale.Get("en");
            string              projectId           = null;
            ProgrammingLanguage programmingLanguage = ProgrammingLanguage.CRAYON;
            List <Wax.BuildArg> remainingBuildArgs  = new List <Wax.BuildArg>();
            List <string>       envFilesBuilder     = new List <string>();
            List <string>       deps = new List <string>();

            foreach (Wax.BuildArg buildArg in buildArgs)
            {
                switch (buildArg.Name)
                {
                case "programmingLanguage":
                    ProgrammingLanguage?pl = ProgrammingLanguageParser.Parse(buildArg.Value);
                    if (pl == null)
                    {
                        throw new InvalidOperationException("Invalid programming language in build file: '" + buildArg.Value + "'.");
                    }
                    programmingLanguage = pl.Value;
                    break;

                case "version":
                    version = buildArg.Value;
                    break;

                case "source":
                    sources.Add(buildArg.Value);
                    break;

                case "icon":
                    icons.Add(buildArg.Value);
                    break;

                case "compilerLocale":
                    compilerLocale = Locale.Get(buildArg.Value);
                    break;

                case "id":
                    projectId = buildArg.Value;
                    break;

                case "dependencies":
                    deps.Add(buildArg.Value);
                    break;

                case "envFile":
                    throw new Exception();     // These are eliminated in the parsing phase.

                default:
                    remainingBuildArgs.Add(buildArg);
                    break;
                }
            }

            if (projectId == null)
            {
                throw new InvalidOperationException("The projectId is not defined in the build file.");
            }
            if (projectId.Length == 0)
            {
                throw new InvalidOperationException("projectId cannot be blank.");
            }
            if (projectId.ToCharArray().Any(c => (c < 'a' || c > 'z') && (c < 'A' || c > 'Z') && (c < '0' || c > '9')))
            {
                throw new InvalidOperationException("projectId can only contain alphanumeric characters.");
            }
            if (projectId[0] >= '0' && projectId[0] <= '9')
            {
                throw new InvalidOperationException("projectId cannot being with a number.");
            }

            pr
            .AddReplacement("PROJECT_ID", projectId)
            .AddReplacement("COMPILER_LANGUAGE", programmingLanguage.ToString().ToUpperInvariant())
            .AddReplacement("VERSION", version)
            .AddReplacement("COMPILER_LOCALE", compilerLocale.ID.ToLowerInvariant());

            ProjectFilePath[] envFiles = ToFilePaths(projectDir, envFilesBuilder);

            BuildContext buildContext = new BuildContext()
            {
                ProjectID               = projectId,
                ProjectDirectory        = projectDir,
                SourceFolders           = ToFilePaths(projectDir, sources),
                Version                 = version,
                RootProgrammingLanguage = programmingLanguage,
                IconFilePaths           = icons
                                          // TODO: icon values should be concatenated within a build target, but override previous targets
                                          .Select(t => pr.Replace(t))
                                          .Select(t => FileUtil.GetAbsolutePathFromRelativeOrAbsolutePath(projectDir, t))
                                          .Select(t => FileUtil.GetCanonicalizeUniversalPath(t))
                                          .Where(t =>
                {
                    if (!System.IO.File.Exists(t))
                    {
                        throw new System.InvalidOperationException("The following icon path does not exist: '" + t + "'.");
                    }
                    return(true);
                })
                                          .ToArray(),
                CompilerLocale = compilerLocale,
                LocalDeps      = deps
                                 .Select(t => Wax.Util.EnvironmentVariables.DoReplacementsInString(t))
                                 .Select(t => pr.Replace(t))
                                 .Select(t => FileUtil.GetCanonicalizeUniversalPath(t))
                                 .ToArray(),
                ExtensionArgs = extensionArgs.Select(arg => { arg.Value = pr.Replace(arg.Value); return(arg); }).ToArray(),
            };

            foreach (Wax.BuildArg buildArg in remainingBuildArgs)
            {
                string value = pr.Replace(buildArg.Value);
                switch (buildArg.Name)
                {
                case "title": buildContext.ProjectTitle = pr.Replace(value); break;

                case "description": buildContext.Description = pr.Replace(value); break;

                case "orientation": buildContext.Orientation = ParseOrientations(value); break;

                case "output": buildContext.OutputFolder = FileUtil.JoinAndCanonicalizePath(projectDir, pr.Replace(value)); break;

                case "delegateMainTo": buildContext.DelegateMainTo = value; break;

                case "removeSymbols": buildContext.RemoveSymbols = GetBoolValue(value); break;

                case "skipRun": buildContext.SkipRun = GetBoolValue(value); break;
                }
            }

            Dictionary <string, BuildVar> buildVarLookup = new Dictionary <string, BuildVar>();

            foreach (BuildVar bv in buildVars)
            {
                buildVarLookup[bv.ID] = bv;
            }
            buildContext.BuildVariableLookup = buildVarLookup;

            return(buildContext);
        }
Ejemplo n.º 2
0
        public static BuildContext Parse(string projectDir, string buildFile, string nullableTargetName, bool useRelativePathsInErrors)
        {
            BuildRoot buildInput = GetBuildRoot(buildFile);
            string    platform   = null;
            Dictionary <string, BuildVarCanonicalized> varLookup;
            string targetName    = nullableTargetName;
            Target desiredTarget = null;

            if (nullableTargetName != null)
            {
                desiredTarget = FindTarget(targetName, buildInput.Targets);

                if (desiredTarget == null)
                {
                    throw new InvalidOperationException("Build target does not exist in build file: '" + targetName + "'.");
                }

                platform = desiredTarget.Platform;
            }
            else
            {
                targetName    = "cbx";
                desiredTarget = FindTarget(targetName, buildInput.Targets) ?? new Target();
            }

            Dictionary <string, string> replacements = new Dictionary <string, string>()
            {
                { "TARGET_NAME", targetName }
            };

            varLookup = BuildVarParser.GenerateBuildVars(buildInput, desiredTarget, replacements);

            if (desiredTarget.HasLegacyIcon || buildInput.HasLegacyIcon)
            {
                // TODO: remove this in 2.2.0 or something
                throw new InvalidOperationException(
                          "This build file has a string property for an icon path. " +
                          "This has been changed to a JSON array of strings for icon paths with a key called \"icons\" instead of \"icon\". " +
                          "Please update your build file accordingly.");
            }

            if (desiredTarget.HasLegacyTitle || buildInput.HasLegacyTitle)
            {
                throw new InvalidOperationException("This build file has a \"default-title\" property, which was changed to just \"title\" in 2.1.0. Please update your build file accordingly.");
            }

            SourceItem[] sources      = desiredTarget.SourcesNonNull.Union(buildInput.SourcesNonNull).ToArray();
            string       output       = desiredTarget.Output ?? buildInput.Output;
            string       projectId    = desiredTarget.ProjectId ?? buildInput.ProjectId;
            string       version      = desiredTarget.Version ?? buildInput.Version ?? "1.0";
            string       jsFilePrefix = desiredTarget.JsFilePrefix ?? buildInput.JsFilePrefix;
            bool         jsFullPage   = NullableBoolean.ToBoolean(desiredTarget.JsFullPageRaw ?? buildInput.JsFullPageRaw, false);

            ImageSheet[] imageSheets = MergeImageSheets(desiredTarget.ImageSheets, buildInput.ImageSheets);
            // TODO: maybe set this default value to true, although this does nothing as of now.
            bool   minified            = NullableBoolean.ToBoolean(desiredTarget.MinifiedRaw ?? buildInput.MinifiedRaw, false);
            bool   exportDebugByteCode = BoolUtil.Parse(desiredTarget.ExportDebugByteCodeRaw ?? buildInput.ExportDebugByteCodeRaw);
            string guidSeed            = desiredTarget.GuidSeed ?? buildInput.GuidSeed ?? "";

            // TODO: make this a string array.
            string[] iconFilePaths   = CombineAndFlattenStringArrays(desiredTarget.IconFilePaths, buildInput.IconFilePaths);
            string   launchScreen    = desiredTarget.LaunchScreen ?? buildInput.LaunchScreen;
            string   projectTitle    = desiredTarget.ProjectTitle ?? buildInput.ProjectTitle;
            string   orientation     = desiredTarget.Orientation ?? buildInput.Orientation;
            string   iosBundlePrefix = desiredTarget.IosBundlePrefix ?? buildInput.IosBundlePrefix;
            string   javaPackage     = desiredTarget.JavaPackage ?? buildInput.JavaPackage;

            string[] localDeps           = CombineAndFlattenStringArrays(desiredTarget.LocalDeps, buildInput.LocalDeps);
            string[] remoteDeps          = CombineAndFlattenStringArrays(desiredTarget.RemoteDeps, buildInput.RemoteDeps);
            string   description         = desiredTarget.Description ?? buildInput.Description ?? "";
            Size     windowSize          = Size.Merge(desiredTarget.WindowSize, buildInput.WindowSize) ?? new Size();
            string   compilerLocale      = desiredTarget.CompilerLocale ?? buildInput.CompilerLocale ?? "en";
            bool     isCSharpCompatMode  = desiredTarget.IsCSharpCompatMode || buildInput.IsCSharpCompatMode; // TODO(acrylic-convert): should have unset state with ??
            string   programmingLanguage = buildInput.ProgrammingLanguage ?? "Crayon";
            string   delegateMainTo      = desiredTarget.DelegateMainTo ?? buildInput.DelegateMainTo;

            if (output == null)
            {
                throw new InvalidOperationException("No output directory defined.");
            }

            PercentReplacer pr = new PercentReplacer()
                                 .AddReplacement("COMPILER_VERSION", VersionInfo.VersionString)
                                 .AddReplacement("COMPILER_LANGUAGE", programmingLanguage)
                                 .AddReplacement("TARGET_NAME", targetName);

            version = pr.Replace(version);
            pr.AddReplacement("VERSION", version);

            compilerLocale = pr.Replace(compilerLocale);
            pr.AddReplacement("COMPILER_LOCALE", compilerLocale);

            output        = FileUtil.GetCanonicalizeUniversalPath(pr.Replace(output));
            projectId     = pr.Replace(projectId);
            jsFilePrefix  = pr.Replace(jsFilePrefix);
            guidSeed      = pr.Replace(guidSeed);
            iconFilePaths = iconFilePaths
                            .Select(t => pr.Replace(t))
                            .Select(t => FileUtil.GetAbsolutePathFromRelativeOrAbsolutePath(projectDir, t))
                            .Select(t => FileUtil.GetCanonicalizeUniversalPath(t))
                            .ToArray();
            launchScreen        = pr.Replace(launchScreen);
            projectTitle        = pr.Replace(projectTitle);
            orientation         = pr.Replace(orientation);
            iosBundlePrefix     = pr.Replace(iosBundlePrefix);
            javaPackage         = pr.Replace(javaPackage);
            programmingLanguage = pr.Replace(programmingLanguage);
            localDeps           = localDeps
                                  .Select(t => CommonUtil.Environment.EnvironmentVariables.DoReplacementsInString(t))
                                  .Select(t => pr.Replace(t))
                                  .Select(t => FileUtil.GetCanonicalizeUniversalPath(t))
                                  .ToArray();
            remoteDeps = remoteDeps
                         .Select(t => pr.Replace(t))
                         .ToArray();
            description = pr.Replace(description);

            BuildContext buildContext = new BuildContext()
            {
                ProjectDirectory          = projectDir,
                JsFilePrefix              = jsFilePrefix,
                OutputFolder              = output,
                Platform                  = platform,
                ProjectID                 = projectId,
                Minified                  = minified,
                ReadableByteCode          = exportDebugByteCode,
                GuidSeed                  = guidSeed,
                IconFilePaths             = iconFilePaths,
                LaunchScreenPath          = launchScreen,
                ProjectTitle              = projectTitle,
                Orientation               = orientation,
                LocalDeps                 = localDeps,
                RemoteDeps                = remoteDeps,
                IosBundlePrefix           = iosBundlePrefix,
                JavaPackage               = javaPackage,
                JsFullPage                = jsFullPage,
                WindowWidth               = windowSize.Width,
                WindowHeight              = windowSize.Height,
                CompilerLocale            = Locale.Get(compilerLocale),
                IsCSharpCompatibilityMode = isCSharpCompatMode,
                DelegateMainTo            = delegateMainTo,
            };

            ProgrammingLanguage?nullableLanguage = ProgrammingLanguageParser.Parse(programmingLanguage);

            if (nullableLanguage == null)
            {
                throw new InvalidOperationException("Invalid programming language specified: '" + programmingLanguage + "'");
            }

            buildContext.TopLevelAssembly = new AssemblyContext(buildContext)
            {
                Description            = description,
                Version                = version,
                SourceFolders          = ToFilePaths(projectDir, sources),
                ImageSheetPrefixesById = imageSheets.ToDictionary <ImageSheet, string, string[]>(s => s.Id, s => s.Prefixes),
                ImageSheetIds          = imageSheets.Select <ImageSheet, string>(s => s.Id).ToArray(),
                BuildVariableLookup    = varLookup,
                ProgrammingLanguage    = nullableLanguage.Value,
            };

            return(buildContext.ValidateValues(useRelativePathsInErrors));
        }