public override void OnInspectorGUI()
        {
            var config = (PackageManifestConfig)target;

            using (var scope = new EditorGUI.ChangeCheckScope())
            {
                EditorGUILayout.LabelField(EditorConstants.PACKAGE_JSON_HEADER, EditorStyles.boldLabel);
                using (new EditorGUI.DisabledScope(true))
                {
                    EditorGUILayout.PropertyField(serializedObject.FindProperty(ID_PROPERTY_NAME));
                }

                EditorGUILayout.PropertyField(serializedObject.FindProperty(NAME_PROPERTY_NAME));
                EditorGUILayout.PropertyField(serializedObject.FindProperty(DISPLAY_NAME_PROPERTY));
                EditorGUILayout.PropertyField(serializedObject.FindProperty(PACKAGE_VERSION_PROPERTY_NAME));
                EditorGUILayout.PropertyField(serializedObject.FindProperty(UNITY_VERSION_PROPERTY_NAME));
                EditorGUILayout.PropertyField(serializedObject.FindProperty(DESCRIPTION_PROPERTY_NAME));
                EditorGUILayout.PropertyField(serializedObject.FindProperty(CATEGORY_PROPERTY_NAME));

                _keywordReorderableList.DoLayoutList();
                _dependenciesReorderableList.DoLayoutList();

                EditorGUILayout.Space();
                EditorGUILayout.LabelField(EditorConstants.PACKAGE_CONTENT_HEADER, EditorStyles.boldLabel);

                _sourcePathsReorderableList.DoLayoutList();
                _excludePathsReorderableList.DoLayoutList();

                // Package Source Export
                using (new EditorGUILayout.HorizontalScope())
                {
                    var destinationPathProperty = serializedObject.FindProperty(DESTINATION_PATH_PROPERTY_NAME);
                    EditorGUILayout.PropertyField(
                        destinationPathProperty,
                        GUILayout.Height(EditorConstants.FOLDER_PATH_PICKER_HEIGHT));
                    GUILayoutTools.DrawFolderPickerLayout(
                        destinationPathProperty,
                        EditorConstants.SELECT_PACKAGE_EXPORT_PATH_PICKER_TITLE);
                }

                // Legacy Package Export
                using (new EditorGUILayout.HorizontalScope())
                {
                    var legacyPackagePathProperty = serializedObject.FindProperty(LEGACY_PACKAGE_PATH_PROPERTY_NAME);
                    EditorGUILayout.PropertyField(
                        legacyPackagePathProperty,
                        GUILayout.Height(EditorConstants.FOLDER_PATH_PICKER_HEIGHT));
                    GUILayoutTools.DrawFolderPickerLayout(
                        legacyPackagePathProperty,
                        EditorConstants.SELECT_PACKAGE_EXPORT_PATH_PICKER_TITLE);
                }

                // Version Constants Export
                using (new EditorGUILayout.VerticalScope(EditorConstants.GROUP_BOX))
                {
                    // Namespace
                    var namespaceProperty = serializedObject.FindProperty(VERSION_CONSTANTS_NAMESPACE_PROPERTY_NAME);
                    EditorGUILayout.PropertyField(namespaceProperty);
                    if (string.IsNullOrEmpty(namespaceProperty.stringValue))
                    {
                        EditorGUILayout.HelpBox(EditorConstants.GLOBAL_NAMESPACE_WARNING, MessageType.Info);
                    }

                    // Output folder
                    using (new EditorGUILayout.HorizontalScope())
                    {
                        var versionConstantsPathProperty = serializedObject.FindProperty(VERSION_CONSTANTS_PATH_PROPERTY_NAME);
                        EditorGUILayout.PropertyField(
                            versionConstantsPathProperty,
                            GUILayout.Height(EditorConstants.FOLDER_PATH_PICKER_HEIGHT));
                        GUILayoutTools.DrawFolderPickerLayout(
                            versionConstantsPathProperty,
                            EditorConstants.SELECT_VERSION_CONSTANTS_PATH_PICKER_TITLE);
                    }
                }

                if (scope.changed)
                {
                    serializedObject.ApplyModifiedProperties();
                }
            }

            EditorGUILayout.Space();
            EditorGUILayout.LabelField(EditorConstants.PACKAGE_ACTIONS_HEADER, EditorStyles.boldLabel);

            if (GUILayout.Button(new GUIContent(
                                     EditorConstants.GENERATE_VERSION_CONSTANTS_BUTTON_TEXT,
                                     EditorConstants.GENERATE_VERSION_CONSTANTS_TOOLTIP)))
            {
                CodeGenTools.GenerateVersionConstants(config);
            }

            if (GUILayout.Button(EditorConstants.UPDATE_PACKAGE_BUTTON_TEXT))
            {
                FileTools.CreateOrUpdatePackageSource(config);
            }

            if (GUILayout.Button(EditorConstants.EXPORT_LEGACY_PACKAGE_BUTTON_TEXT))
            {
                UnityFileTools.CompileLegacyPackage(config);
            }
        }
        /// <summary>
        /// Attempts to use zero or more <see cref="PackageManifestConfig"/> assets to generate legacy Unity packages
        /// and Unity source.
        /// </summary>
        public static void Generate()
        {
            Debug.Log(CI_STARTING);

            try
            {
                EditorApplication.LockReloadAssemblies();
                AssetDatabase.StartAssetEditing();

                // Get command line args and log them
                var commandLineArgs = CommandLineTools.GetKVPCommandLineArguments();

                SB.Clear();
                const string CLI_ARG_FORMAT = "{0} => {1}";
                foreach (var commandLineArg in commandLineArgs)
                {
                    SB.AppendFormat(CLI_ARG_FORMAT, commandLineArg.Key, commandLineArg.Value);
                    SB.AppendLine();
                }

                Debug.LogFormat(CI_COMMAND_LINE_ARGS_PARSED_FORMAT, SB.ToString());

                // Attempt to parse CLI-passed version, if present.
                string version = null;
                if (commandLineArgs.TryGetValue(VERSION_ARG_KEY, out var versionRawValue))
                {
                    version = (string)versionRawValue;
                }

                // See if we should generate version constants
                var generateVersionConstants = false;
                if (commandLineArgs.TryGetValue(
                        GENERATE_VERSION_CONSTANTS_ARG_KEY,
                        out var generateVersionConstantsRawValue))
                {
                    generateVersionConstants = bool.Parse(generateVersionConstantsRawValue.ToString());
                }

                // Get all package manifests in project
                var allPackageManifestConfigs = PackageManifestTools.GetAllConfigs();

                Debug.LogFormat(CI_FOUND_CONFIGS, allPackageManifestConfigs.Length);

                // Check to see if any IDs have been passed for specific configs
                string[] configIds;
                if (commandLineArgs.ContainsKey(ID_ARG_KEY))
                {
                    Debug.Log(CI_USING_ONLY_CONFIGS_FROM_ARG);

                    const char COMMA_CHAR = ',';
                    var        idArgValue = commandLineArgs[ID_ARG_KEY].ToString();
                    configIds = idArgValue.Split(COMMA_CHAR);
                }
                // Otherwise generate all package manifest configs in project.
                else
                {
                    Debug.Log(CI_USING_ALL_CONFIGS);

                    configIds = allPackageManifestConfigs.Select(x => x.Id).ToArray();
                }

                // For each matching config ID, find the matching package manifest config and generate any relevant packages.
                Debug.Log(CI_GENERATION_STARTING);
                foreach (var configId in configIds)
                {
                    var matchingConfig = allPackageManifestConfigs.FirstOrDefault(x =>
                                                                                  string.Compare(x.Id, configId, StringComparison.OrdinalIgnoreCase) == 0);

                    // If a config cannot be found matching config id, skip it and continue.
                    if (matchingConfig == null)
                    {
                        Debug.LogWarningFormat(CI_PACKAGE_NOT_FOUND_FORMAT, configId);

                        continue;
                    }

                    var configName = matchingConfig.name;

                    Debug.LogFormat(CI_PACKAGE_FOUND_FORMAT, configName, configId);

                    // Set the CLI passed version if present, otherwise default to checked in version number.
                    if (!string.IsNullOrEmpty(version))
                    {
                        matchingConfig.packageVersion = version;

                        EditorUtility.SetDirty(matchingConfig);
                    }

                    // If set to generate version constants, do so.
                    if (generateVersionConstants)
                    {
                        CodeGenTools.GenerateVersionConstants(matchingConfig);
                    }

                    // Otherwise generate the corresponding legacy unity package and package source if their output paths
                    // have been defined
                    if (!string.IsNullOrEmpty(matchingConfig.legacyPackageDestinationPath))
                    {
                        Debug.LogFormat(CI_GENERATING_LEGACY_PACKAGE_FORMAT, configName, configId);

                        UnityFileTools.CompileLegacyPackage(matchingConfig);
                    }
                    else
                    {
                        Debug.LogFormat(CI_SKIPPING_LEGACY_PACKAGE_FORMAT, configName, configId);
                    }

                    if (!string.IsNullOrEmpty(matchingConfig.packageDestinationPath))
                    {
                        Debug.LogFormat(CI_GENERATING_PACKAGE_SOURCE_FORMAT, configName, configId);

                        FileTools.CreateOrUpdatePackageSource(matchingConfig);
                    }
                    else
                    {
                        Debug.LogFormat(CI_SKIPPING_PACKAGE_SOURCE_FORMAT, configName, configId);
                    }
                }
            }
            catch (Exception e)
            {
                Debug.LogErrorFormat("An unexpected error occured during package generation:\n\n{0}", e);
            }
            finally
            {
                AssetDatabase.StopAssetEditing();
                EditorApplication.UnlockReloadAssemblies();
            }

            Debug.Log(CI_COMPLETE);
        }