com.elestrago.unity.package-tools 2.5.2 → 2.5.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (28) hide show
  1. package/CAHNGELOG.md +21 -0
  2. package/Documentation~/api.md +156 -66
  3. package/Documentation~/examples/ciutils-exportlegacypackage.md +32 -0
  4. package/Documentation~/examples/ciutils-exportpackagesource.md +38 -0
  5. package/Documentation~/examples/ciutils-generate.md +13 -8
  6. package/Documentation~/examples/ciutils-listconfigs.md +36 -0
  7. package/Documentation~/examples/packagetoolapi-exportlegacypackage.md +39 -0
  8. package/Documentation~/examples/packagetoolapi-exportpackagesource.md +40 -0
  9. package/Documentation~/examples/packagetoolapi-findconfig.md +46 -0
  10. package/Documentation~/examples/packagetoolapi-generateversionconstants.md +42 -0
  11. package/Documentation~/examples/packagetoolapi-getconfig.md +37 -0
  12. package/Documentation~/examples/packagetoolapi-getconfigs.md +36 -0
  13. package/Documentation~/examples/preparedll.md +1 -1
  14. package/Documentation~/examples.md +12 -1
  15. package/Documentation~/manual.md +39 -23
  16. package/Editor/CIUtils.cs +273 -74
  17. package/Editor/EditorConstants.cs +21 -1
  18. package/Editor/Inspectors/PackageManifestConfigInspector.cs +16 -3
  19. package/Editor/PackageToolApi.cs +141 -0
  20. package/Editor/PackageToolApi.cs.meta +11 -0
  21. package/Editor/Tools/CIResultModel.cs +107 -0
  22. package/Editor/Tools/CIResultModel.cs.meta +11 -0
  23. package/Editor/Tools/CodeGenTools.cs +8 -2
  24. package/Editor/Tools/FileTools.cs +76 -46
  25. package/Editor/Tools/UnityFileTools.cs +16 -2
  26. package/README.md +17 -8
  27. package/Samples~/ClaudeSkills/unity-package-docs/SKILL.md +6 -1
  28. package/package.json +4 -4
package/Editor/CIUtils.cs CHANGED
@@ -1,4 +1,5 @@
1
- using System;
1
+ using System;
2
+ using System.Collections.Generic;
2
3
  using System.IO;
3
4
  using System.Linq;
4
5
  using System.Text;
@@ -9,7 +10,8 @@ using UnityEngine;
9
10
  namespace PackageTool
10
11
  {
11
12
  /// <summary>
12
- /// Continuous-integration API for package tools.
13
+ /// Continuous-integration API for package tools. Every entry point here is meant to be invoked through
14
+ /// Unity's -executeMethod and reads its arguments as key=value command-line pairs.
13
15
  /// </summary>
14
16
  public static class CIUtils
15
17
  {
@@ -18,6 +20,8 @@ namespace PackageTool
18
20
  private const string VERSION_ARG_KEY = "version";
19
21
  private const string PREVIEW_ARG_KEY = "preview";
20
22
  private const string GENERATE_VERSION_CONSTANTS_ARG_KEY = "generateversionconstants";
23
+ private const string RESULT_PATH_ARG_KEY = "resultpath";
24
+ private const string EXIT_ON_FINISH_ARG_KEY = "exitonfinish";
21
25
 
22
26
  // Logs
23
27
  private const string LOG_PREFIX = "[Package Tools] ";
@@ -49,6 +53,10 @@ namespace PackageTool
49
53
  private const string CI_SKIPPING_PACKAGE_SOURCE_FORMAT = LOG_PREFIX +
50
54
  "Skipping Package Source for Config [{0}] with ID: [{1}] as no output path is present in config.";
51
55
 
56
+ private const string CI_LISTING_CONFIGS = LOG_PREFIX + "Listing [{0}] configs in project.";
57
+ private const string CI_EXPORT_FAILED_FORMAT = LOG_PREFIX + "Export failed for [{0}]:\n\n{1}";
58
+ private const string CI_UNEXPECTED_ERROR_FORMAT = "An unexpected error occured during package generation:\n\n{0}";
59
+
52
60
  private static readonly StringBuilder StringBuilder;
53
61
 
54
62
  private static bool IsRunningOnCI => !string.IsNullOrEmpty(Environment.GetEnvironmentVariable("CI"));
@@ -66,123 +74,232 @@ namespace PackageTool
66
74
  {
67
75
  Debug.Log(CI_STARTING);
68
76
 
69
- PrepareDll();
77
+ PrepareDllInternal();
78
+
79
+ var commandLineArgs = GetLoggedCommandLineArguments();
80
+ var results = new List<CIResultModel>();
81
+ var hasError = false;
70
82
 
71
83
  try
72
84
  {
73
85
  EditorApplication.LockReloadAssemblies();
74
86
  AssetDatabase.StartAssetEditing();
75
87
 
76
- // Get command line args and log them
77
- var commandLineArgs = CommandLineTools.GetKVPCommandLineArguments();
78
-
79
- StringBuilder.Clear();
80
- const string cliArgFormat = "{0} => {1}";
81
- foreach (var commandLineArg in commandLineArgs)
82
- {
83
- StringBuilder.AppendFormat(cliArgFormat, commandLineArg.Key, commandLineArg.Value);
84
- StringBuilder.AppendLine();
85
- }
86
-
87
- Debug.LogFormat(CI_COMMAND_LINE_ARGS_PARSED_FORMAT, StringBuilder);
88
-
89
88
  // Attempt to parse CLI-passed version, if present.
90
- string version = null;
91
- if (commandLineArgs.TryGetValue(VERSION_ARG_KEY, out var versionRawValue))
92
- version = (string)versionRawValue;
89
+ var version = GetStringArgument(commandLineArgs, VERSION_ARG_KEY);
93
90
 
94
- // Attempt to parse CLI-passed version, if present.
91
+ // Attempt to parse CLI-passed preview flag, if present.
95
92
  bool? isPreview = null;
96
93
  if (commandLineArgs.TryGetValue(PREVIEW_ARG_KEY, out var previewRawValue))
97
94
  isPreview = bool.Parse((string)previewRawValue);
98
95
 
99
96
  // See if we should generate version constants
100
- var generateVersionConstants = false;
101
- if (commandLineArgs.TryGetValue(
102
- GENERATE_VERSION_CONSTANTS_ARG_KEY,
103
- out var generateVersionConstantsRawValue))
104
- generateVersionConstants = bool.Parse(generateVersionConstantsRawValue.ToString());
97
+ var generateVersionConstants = GetBoolArgument(commandLineArgs, GENERATE_VERSION_CONSTANTS_ARG_KEY);
105
98
 
106
99
  // Get all package manifests in project
107
- var allPackageManifestConfigs = PackageManifestTools.GetAllConfigs();
100
+ var allPackageManifestConfigs = PackageToolApi.GetConfigs();
108
101
 
109
102
  Debug.LogFormat(CI_FOUND_CONFIGS, allPackageManifestConfigs.Length);
110
103
 
111
104
  // Check to see if any IDs have been passed for specific configs
112
- string[] configIds;
113
- if (commandLineArgs.TryGetValue(ID_ARG_KEY, out var arg))
114
- {
115
- Debug.Log(CI_USING_ONLY_CONFIGS_FROM_ARG);
116
-
117
- const char commaChar = ',';
118
- var idArgValue = arg.ToString();
119
- configIds = idArgValue.Split(commaChar);
120
- }
121
- // Otherwise generate all package manifest configs in project.
122
- else
123
- {
124
- Debug.Log(CI_USING_ALL_CONFIGS);
105
+ var selectors = GetSelectors(commandLineArgs, allPackageManifestConfigs);
125
106
 
126
- configIds = allPackageManifestConfigs.Select(x => x.Id).ToArray();
127
- }
128
-
129
- // For each matching config ID, find the matching package manifest config and generate any relevant packages.
107
+ // For each matching selector, find the matching package manifest config and generate any relevant packages.
130
108
  Debug.Log(CI_GENERATION_STARTING);
131
- foreach (var configId in configIds)
109
+ foreach (var selector in selectors)
132
110
  {
133
- var matchingConfig = allPackageManifestConfigs.FirstOrDefault(x =>
134
- string.Compare(x.Id, configId, StringComparison.OrdinalIgnoreCase) == 0);
111
+ var matchingConfig = PackageToolApi.FindConfig(selector, allPackageManifestConfigs);
135
112
 
136
- // If a config cannot be found matching config id, skip it and continue.
113
+ // If a config cannot be found matching the selector, skip it and continue.
137
114
  if (matchingConfig == null)
138
115
  {
139
- Debug.LogWarningFormat(CI_PACKAGE_NOT_FOUND_FORMAT, configId);
116
+ Debug.LogWarningFormat(CI_PACKAGE_NOT_FOUND_FORMAT, selector);
117
+ results.Add(CIResultModel.CreateFailure(
118
+ selector,
119
+ string.Format(EditorConstants.CONFIG_NOT_FOUND_ERROR_FORMAT, selector)));
140
120
 
141
121
  continue;
142
122
  }
143
123
 
144
124
  var configName = matchingConfig.name;
125
+ var configId = matchingConfig.Id;
126
+ var result = CIResultModel.CreateForConfig(selector, matchingConfig);
127
+ results.Add(result);
145
128
 
146
129
  Debug.LogFormat(CI_PACKAGE_FOUND_FORMAT, configName, configId);
147
130
 
148
- // Set the CLI passed version if present, otherwise default to checked in version number.
149
- if (!string.IsNullOrEmpty(version))
131
+ try
150
132
  {
151
- matchingConfig.packageVersion = version;
152
- EditorUtility.SetDirty(matchingConfig);
133
+ // If set to generate version constants, do so.
134
+ if (generateVersionConstants)
135
+ result.VersionConstantsPath = PackageToolApi.GenerateVersionConstants(matchingConfig);
136
+
137
+ // Otherwise generate the corresponding legacy unity package and package source if their
138
+ // output paths have been defined
139
+ if (!string.IsNullOrEmpty(matchingConfig.legacyPackageDestinationPath))
140
+ {
141
+ Debug.LogFormat(CI_GENERATING_LEGACY_PACKAGE_FORMAT, configName, configId);
142
+
143
+ result.LegacyPackagePath = PackageToolApi.ExportLegacyPackage(matchingConfig, version);
144
+ }
145
+ else
146
+ {
147
+ Debug.LogFormat(CI_SKIPPING_LEGACY_PACKAGE_FORMAT, configName, configId);
148
+ }
149
+
150
+ if (!string.IsNullOrEmpty(matchingConfig.packageDestinationPath))
151
+ {
152
+ Debug.LogFormat(CI_GENERATING_PACKAGE_SOURCE_FORMAT, configName, configId);
153
+
154
+ result.PackageSourcePath = PackageToolApi.ExportPackageSource(matchingConfig, version);
155
+ }
156
+ else
157
+ {
158
+ Debug.LogFormat(CI_SKIPPING_PACKAGE_SOURCE_FORMAT, configName, configId);
159
+ }
160
+
161
+ result.PackageVersion = matchingConfig.packageVersion;
153
162
  }
163
+ catch (Exception e)
164
+ {
165
+ result.Success = false;
166
+ result.Error = e.Message;
167
+ hasError = true;
168
+
169
+ Debug.LogErrorFormat(CI_EXPORT_FAILED_FORMAT, configName, e);
170
+ }
171
+ }
172
+ }
173
+ catch (Exception e)
174
+ {
175
+ results.Add(CIResultModel.CreateFailure(null, e.Message));
176
+ hasError = true;
177
+
178
+ Debug.LogErrorFormat(CI_UNEXPECTED_ERROR_FORMAT, e);
179
+ }
180
+ finally
181
+ {
182
+ AssetDatabase.StopAssetEditing();
183
+ EditorApplication.UnlockReloadAssemblies();
184
+ }
185
+
186
+ Debug.Log(CI_COMPLETE);
187
+
188
+ // Generate has always logged and continued on a missing config, so it only fails on a real error.
189
+ Finish(commandLineArgs, results, !hasError);
190
+ }
191
+
192
+ /// <summary>
193
+ /// Writes every <see cref="PackageManifestConfig"/> in the project to the file passed in the
194
+ /// 'resultpath' argument so that callers can discover which selectors are valid.
195
+ /// </summary>
196
+ public static void ListConfigs()
197
+ {
198
+ var commandLineArgs = GetLoggedCommandLineArguments();
199
+ var configs = PackageToolApi.GetConfigs();
200
+
201
+ Debug.LogFormat(CI_LISTING_CONFIGS, configs.Length);
154
202
 
155
- // If set to generate version constants, do so.
156
- if (generateVersionConstants) CodeGenTools.GenerateVersionConstants(matchingConfig);
203
+ var models = configs.Select(CIConfigModel.CreateFromConfig).ToList();
157
204
 
158
- // Otherwise generate the corresponding legacy unity package and package source if their output paths
159
- // have been defined
160
- if (!string.IsNullOrEmpty(matchingConfig.legacyPackageDestinationPath))
205
+ CIResultTools.WriteResultFile(GetStringArgument(commandLineArgs, RESULT_PATH_ARG_KEY), models);
206
+
207
+ ExitIfRequested(commandLineArgs, true);
208
+ }
209
+
210
+ /// <summary>
211
+ /// Exports the package source of every selected <see cref="PackageManifestConfig"/>.
212
+ /// </summary>
213
+ public static void ExportPackageSource() => RunExport(exportSource: true, exportLegacy: false);
214
+
215
+ /// <summary>
216
+ /// Exports every selected <see cref="PackageManifestConfig"/> as a legacy unity package.
217
+ /// </summary>
218
+ public static void ExportLegacyPackage() => RunExport(exportSource: false, exportLegacy: true);
219
+
220
+ private static void RunExport(bool exportSource, bool exportLegacy)
221
+ {
222
+ var commandLineArgs = GetLoggedCommandLineArguments();
223
+ var results = new List<CIResultModel>();
224
+
225
+ try
226
+ {
227
+ EditorApplication.LockReloadAssemblies();
228
+ AssetDatabase.StartAssetEditing();
229
+
230
+ var version = GetStringArgument(commandLineArgs, VERSION_ARG_KEY);
231
+ var generateVersionConstants = GetBoolArgument(commandLineArgs, GENERATE_VERSION_CONSTANTS_ARG_KEY);
232
+ var allConfigs = PackageToolApi.GetConfigs();
233
+
234
+ Debug.LogFormat(CI_FOUND_CONFIGS, allConfigs.Length);
235
+
236
+ foreach (var selector in GetSelectors(commandLineArgs, allConfigs))
237
+ {
238
+ PackageManifestConfig config;
239
+ try
240
+ {
241
+ config = PackageToolApi.FindConfig(selector, allConfigs);
242
+ }
243
+ catch (Exception e)
161
244
  {
162
- Debug.LogFormat(CI_GENERATING_LEGACY_PACKAGE_FORMAT, configName, configId);
245
+ results.Add(CIResultModel.CreateFailure(selector, e.Message));
163
246
 
164
- UnityFileTools.CompileLegacyPackage(matchingConfig);
247
+ Debug.LogErrorFormat(CI_EXPORT_FAILED_FORMAT, selector, e);
248
+
249
+ continue;
165
250
  }
166
- else
251
+
252
+ // Unlike Generate, asking for a package that is not there is a failure and not a skip.
253
+ if (config == null)
167
254
  {
168
- Debug.LogFormat(CI_SKIPPING_LEGACY_PACKAGE_FORMAT, configName, configId);
255
+ var error = string.Format(EditorConstants.CONFIG_NOT_FOUND_ERROR_FORMAT, selector);
256
+ results.Add(CIResultModel.CreateFailure(selector, error));
257
+
258
+ Debug.LogError(error);
259
+
260
+ continue;
169
261
  }
170
262
 
171
- if (!string.IsNullOrEmpty(matchingConfig.packageDestinationPath))
263
+ var result = CIResultModel.CreateForConfig(selector, config);
264
+ results.Add(result);
265
+
266
+ Debug.LogFormat(CI_PACKAGE_FOUND_FORMAT, config.name, config.Id);
267
+
268
+ try
172
269
  {
173
- Debug.LogFormat(CI_GENERATING_PACKAGE_SOURCE_FORMAT, configName, configId);
270
+ if (generateVersionConstants)
271
+ result.VersionConstantsPath = PackageToolApi.GenerateVersionConstants(config);
272
+
273
+ if (exportLegacy)
274
+ {
275
+ Debug.LogFormat(CI_GENERATING_LEGACY_PACKAGE_FORMAT, config.name, config.Id);
276
+
277
+ result.LegacyPackagePath = PackageToolApi.ExportLegacyPackage(config, version);
278
+ }
279
+
280
+ if (exportSource)
281
+ {
282
+ Debug.LogFormat(CI_GENERATING_PACKAGE_SOURCE_FORMAT, config.name, config.Id);
174
283
 
175
- FileTools.CreateOrUpdatePackageSource(matchingConfig);
284
+ result.PackageSourcePath = PackageToolApi.ExportPackageSource(config, version);
285
+ }
286
+
287
+ result.PackageVersion = config.packageVersion;
176
288
  }
177
- else
289
+ catch (Exception e)
178
290
  {
179
- Debug.LogFormat(CI_SKIPPING_PACKAGE_SOURCE_FORMAT, configName, configId);
291
+ result.Success = false;
292
+ result.Error = e.Message;
293
+
294
+ Debug.LogErrorFormat(CI_EXPORT_FAILED_FORMAT, config.name, e);
180
295
  }
181
296
  }
182
297
  }
183
298
  catch (Exception e)
184
299
  {
185
- Debug.LogErrorFormat("An unexpected error occured during package generation:\n\n{0}", e);
300
+ results.Add(CIResultModel.CreateFailure(null, e.Message));
301
+
302
+ Debug.LogErrorFormat(CI_UNEXPECTED_ERROR_FORMAT, e);
186
303
  }
187
304
  finally
188
305
  {
@@ -190,17 +307,30 @@ namespace PackageTool
190
307
  EditorApplication.UnlockReloadAssemblies();
191
308
  }
192
309
 
193
- Debug.Log(CI_COMPLETE);
310
+ Finish(commandLineArgs, results, results.Count > 0 && results.All(x => x.Success));
194
311
  }
195
312
 
313
+ /// <summary>
314
+ /// Sets the platform compatibility of every plugin in the source path of the project's
315
+ /// <see cref="PackageManifestConfig"/> so that editor assemblies are not shipped as runtime ones.
316
+ /// This is a command-line entry point in its own right, so it exits the editor when running on CI.
317
+ /// </summary>
196
318
  [MenuItem("Tools/PackageTools/PrepareDll")]
197
319
  public static void PrepareDll()
320
+ {
321
+ var success = PrepareDllInternal();
322
+
323
+ if (IsRunningOnCI)
324
+ EditorApplication.Exit(success ? 0 : 1);
325
+ }
326
+
327
+ private static bool PrepareDllInternal()
198
328
  {
199
329
  try
200
330
  {
201
331
  Debug.Log($"[{nameof(CIUtils)}] PrepareDll");
202
332
 
203
- var packageManifestConfig = PackageManifestTools.GetAllConfigs().Single();
333
+ var packageManifestConfig = PackageToolApi.GetConfigs().Single();
204
334
  var sourcePath = packageManifestConfig.sourcePath;
205
335
 
206
336
  Debug.Log($"[{nameof(CIUtils)}] Find assets in path: {sourcePath}");
@@ -231,17 +361,86 @@ namespace PackageTool
231
361
  }
232
362
 
233
363
  Debug.Log($"[{nameof(CIUtils)}] Complete dll import");
364
+
365
+ return true;
234
366
  }
235
367
  catch (Exception e)
236
368
  {
237
369
  Debug.LogException(e);
238
370
 
239
- if (IsRunningOnCI)
240
- EditorApplication.Exit(1);
371
+ return false;
241
372
  }
373
+ }
242
374
 
243
- if (IsRunningOnCI)
244
- EditorApplication.Exit(0);
375
+ private static Dictionary<string, object> GetLoggedCommandLineArguments()
376
+ {
377
+ var commandLineArgs = CommandLineTools.GetKVPCommandLineArguments();
378
+
379
+ StringBuilder.Clear();
380
+ const string cliArgFormat = "{0} => {1}";
381
+ foreach (var commandLineArg in commandLineArgs)
382
+ {
383
+ StringBuilder.AppendFormat(cliArgFormat, commandLineArg.Key, commandLineArg.Value);
384
+ StringBuilder.AppendLine();
385
+ }
386
+
387
+ Debug.LogFormat(CI_COMMAND_LINE_ARGS_PARSED_FORMAT, StringBuilder);
388
+
389
+ return commandLineArgs;
390
+ }
391
+
392
+ /// <summary>
393
+ /// Returns the selectors passed in the 'id' argument, or a selector for every config in the project
394
+ /// when the argument is absent. A selector is a config id, a package name or a config asset name.
395
+ /// </summary>
396
+ private static string[] GetSelectors(
397
+ Dictionary<string, object> commandLineArgs,
398
+ PackageManifestConfig[] allConfigs)
399
+ {
400
+ if (commandLineArgs.TryGetValue(ID_ARG_KEY, out var arg) && arg != null)
401
+ {
402
+ Debug.Log(CI_USING_ONLY_CONFIGS_FROM_ARG);
403
+
404
+ const char commaChar = ',';
405
+
406
+ return arg.ToString()
407
+ .Split(commaChar)
408
+ .Select(x => x.Trim())
409
+ .Where(x => !string.IsNullOrEmpty(x))
410
+ .ToArray();
411
+ }
412
+
413
+ Debug.Log(CI_USING_ALL_CONFIGS);
414
+
415
+ return allConfigs.Select(x => x.Id).ToArray();
416
+ }
417
+
418
+ private static string GetStringArgument(Dictionary<string, object> commandLineArgs, string key)
419
+ {
420
+ return commandLineArgs.TryGetValue(key, out var rawValue) ? (string)rawValue : null;
421
+ }
422
+
423
+ private static bool GetBoolArgument(Dictionary<string, object> commandLineArgs, string key)
424
+ {
425
+ return commandLineArgs.TryGetValue(key, out var rawValue) && bool.Parse(rawValue.ToString());
426
+ }
427
+
428
+ private static void Finish(
429
+ Dictionary<string, object> commandLineArgs,
430
+ IReadOnlyList<CIResultModel> results,
431
+ bool success)
432
+ {
433
+ // The result file is always written before a potential exit so callers never race the editor.
434
+ CIResultTools.WriteResultFile(GetStringArgument(commandLineArgs, RESULT_PATH_ARG_KEY), results);
435
+
436
+ ExitIfRequested(commandLineArgs, success);
437
+ }
438
+
439
+ private static void ExitIfRequested(Dictionary<string, object> commandLineArgs, bool success)
440
+ {
441
+ var exitOnFinish = IsRunningOnCI || GetBoolArgument(commandLineArgs, EXIT_ON_FINISH_ARG_KEY);
442
+ if (exitOnFinish)
443
+ EditorApplication.Exit(success ? 0 : 1);
245
444
  }
246
445
  }
247
- }
446
+ }
@@ -41,7 +41,6 @@ namespace PackageTool
41
41
  public const string ASSET_EXTENSION = ".asset";
42
42
  public const string META_EXTENSION = ".meta";
43
43
  public const string META_FORMAT = "{0}" + META_EXTENSION;
44
- public const string GENERATED_FOLDER_NAME = "Generated";
45
44
  public const string UNITY_PACKAGE_NAME_FORMAT = "{0}_v{1}.unityPackage";
46
45
  public const char EMPTY_SPACE = ' ';
47
46
  public const char UNDERSCORE = '_';
@@ -105,6 +104,27 @@ namespace PackageTool
105
104
  public const string PACKAGE_UPDATE_SUCCESS_FORMAT =
106
105
  "[Package Tools] Successfully updated package source for [{0}].";
107
106
 
107
+ public const string PACKAGE_JSON_META_MISSING_FORMAT =
108
+ "[Package Tools] No meta file was created for [{0}]; it will be missing from the exported package. " +
109
+ "Run the export again once Unity has finished importing.";
110
+
111
+ public const string NO_SOURCE_PATH_ERROR_FORMAT =
112
+ "[Package Tools] Config [{0}] has no source path set.";
113
+
114
+ public const string NO_PACKAGE_DESTINATION_PATH_ERROR_FORMAT =
115
+ "[Package Tools] Config [{0}] has no package destination path set.";
116
+
117
+ public const string NO_LEGACY_DESTINATION_PATH_ERROR_FORMAT =
118
+ "[Package Tools] Config [{0}] has no legacy package destination path set.";
119
+
120
+ public const string CONFIG_NOT_FOUND_ERROR_FORMAT =
121
+ "[Package Tools] No PackageManifestConfig matches [{0}]. Selectors match the config id, the package " +
122
+ "name or the asset name.";
123
+
124
+ public const string AMBIGUOUS_CONFIG_ERROR_FORMAT =
125
+ "[Package Tools] Selector [{0}] matches more than one PackageManifestConfig: {1}. Use the config id " +
126
+ "instead.";
127
+
108
128
  public const string AGENT_LOCKED_FILE_MARKER_FORMAT =
109
129
  "<!--\n" +
110
130
  "AGENT NOTE: This file is auto-generated and locked for direct edits.\n" +
@@ -22,6 +22,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22
22
  SOFTWARE.
23
23
  */
24
24
 
25
+ using System;
25
26
  using PackageTool.Tools;
26
27
  using UnityEditor;
27
28
  using UnityEditorInternal;
@@ -279,17 +280,29 @@ namespace PackageTool.Inspectors
279
280
  EditorConstants.GENERATE_VERSION_CONSTANTS_BUTTON_TEXT,
280
281
  EditorConstants.GENERATE_VERSION_CONSTANTS_TOOLTIP)))
281
282
  {
282
- CodeGenTools.GenerateVersionConstants(config);
283
+ RunAction(config, PackageToolApi.GenerateVersionConstants);
283
284
  }
284
285
 
285
286
  if (GUILayout.Button(EditorConstants.UPDATE_PACKAGE_BUTTON_TEXT))
286
287
  {
287
- FileTools.CreateOrUpdatePackageSource(config);
288
+ RunAction(config, x => PackageToolApi.ExportPackageSource(x));
288
289
  }
289
290
 
290
291
  if (GUILayout.Button(EditorConstants.EXPORT_LEGACY_PACKAGE_BUTTON_TEXT))
291
292
  {
292
- UnityFileTools.CompileLegacyPackage(config);
293
+ RunAction(config, x => PackageToolApi.ExportLegacyPackage(x));
294
+ }
295
+ }
296
+
297
+ private static void RunAction(PackageManifestConfig config, Func<PackageManifestConfig, string> action)
298
+ {
299
+ try
300
+ {
301
+ action.Invoke(config);
302
+ }
303
+ catch (Exception e)
304
+ {
305
+ Debug.LogException(e, config);
293
306
  }
294
307
  }
295
308
 
@@ -0,0 +1,141 @@
1
+ using System;
2
+ using System.Collections.Generic;
3
+ using System.Linq;
4
+ using PackageTool.Tools;
5
+ using UnityEditor;
6
+
7
+ namespace PackageTool
8
+ {
9
+ /// <summary>
10
+ /// Public entry points for driving the package tools from other editor code. Every method throws on
11
+ /// failure and returns the path of what it produced.
12
+ /// </summary>
13
+ public static class PackageToolApi
14
+ {
15
+ /// <summary>
16
+ /// Returns all <see cref="PackageManifestConfig"/> assets in the project.
17
+ /// </summary>
18
+ public static PackageManifestConfig[] GetConfigs() => PackageManifestTools.GetAllConfigs();
19
+
20
+ /// <summary>
21
+ /// Returns the <see cref="PackageManifestConfig"/> matching <paramref name="selector"/>, which is
22
+ /// compared case-insensitively against the config id, the package name and the asset name, in that
23
+ /// order. Returns null when nothing matches and throws when the selector is ambiguous.
24
+ /// </summary>
25
+ /// <param name="selector"></param>
26
+ public static PackageManifestConfig FindConfig(string selector) => FindConfig(selector, GetConfigs());
27
+
28
+ /// <summary>
29
+ /// Returns the <see cref="PackageManifestConfig"/> matching <paramref name="selector"/> from
30
+ /// <paramref name="configs"/>. Use this overload to resolve several selectors against a single lookup
31
+ /// of the project.
32
+ /// </summary>
33
+ /// <param name="selector"></param>
34
+ /// <param name="configs"></param>
35
+ public static PackageManifestConfig FindConfig(string selector, PackageManifestConfig[] configs)
36
+ {
37
+ if (string.IsNullOrEmpty(selector))
38
+ throw new ArgumentNullException(nameof(selector));
39
+ if (configs == null)
40
+ throw new ArgumentNullException(nameof(configs));
41
+
42
+ var matches = MatchByField(configs, selector, config => config.Id);
43
+ if (matches.Count == 0)
44
+ matches = MatchByField(configs, selector, config => config.packageName);
45
+ if (matches.Count == 0)
46
+ matches = MatchByField(configs, selector, config => config.name);
47
+
48
+ if (matches.Count > 1)
49
+ {
50
+ var candidates = string.Join(", ", matches.Select(x => $"{x.name} ({x.Id})"));
51
+ throw new InvalidOperationException(
52
+ string.Format(EditorConstants.AMBIGUOUS_CONFIG_ERROR_FORMAT, selector, candidates));
53
+ }
54
+
55
+ return matches.Count == 1 ? matches[0] : null;
56
+ }
57
+
58
+ /// <summary>
59
+ /// Returns the <see cref="PackageManifestConfig"/> matching <paramref name="selector"/> and throws
60
+ /// when no config matches it.
61
+ /// </summary>
62
+ /// <param name="selector"></param>
63
+ public static PackageManifestConfig GetConfig(string selector)
64
+ {
65
+ var config = FindConfig(selector);
66
+ if (config == null)
67
+ throw new InvalidOperationException(
68
+ string.Format(EditorConstants.CONFIG_NOT_FOUND_ERROR_FORMAT, selector));
69
+
70
+ return config;
71
+ }
72
+
73
+ /// <summary>
74
+ /// Generates the VersionConstants.cs file for <paramref name="config"/> and returns its path, or null
75
+ /// when the config has no version constants path set.
76
+ /// </summary>
77
+ /// <param name="config"></param>
78
+ public static string GenerateVersionConstants(PackageManifestConfig config)
79
+ {
80
+ if (config == null)
81
+ throw new ArgumentNullException(nameof(config));
82
+
83
+ return CodeGenTools.GenerateVersionConstants(config);
84
+ }
85
+
86
+ /// <summary>
87
+ /// Exports the package source of <paramref name="config"/> to its
88
+ /// <see cref="PackageManifestConfig.packageDestinationPath"/> and returns the path of the written
89
+ /// package.json. When <paramref name="version"/> is set it replaces the version on the config asset
90
+ /// before the export.
91
+ /// </summary>
92
+ /// <param name="config"></param>
93
+ /// <param name="version"></param>
94
+ public static string ExportPackageSource(PackageManifestConfig config, string version = null)
95
+ {
96
+ if (config == null)
97
+ throw new ArgumentNullException(nameof(config));
98
+
99
+ ApplyVersion(config, version);
100
+
101
+ return FileTools.CreateOrUpdatePackageSourceOrThrow(config);
102
+ }
103
+
104
+ /// <summary>
105
+ /// Exports <paramref name="config"/> as a legacy unity package to its
106
+ /// <see cref="PackageManifestConfig.legacyPackageDestinationPath"/> and returns the path of the
107
+ /// written file. When <paramref name="version"/> is set it replaces the version on the config asset
108
+ /// before the export.
109
+ /// </summary>
110
+ /// <param name="config"></param>
111
+ /// <param name="version"></param>
112
+ public static string ExportLegacyPackage(PackageManifestConfig config, string version = null)
113
+ {
114
+ if (config == null)
115
+ throw new ArgumentNullException(nameof(config));
116
+
117
+ ApplyVersion(config, version);
118
+
119
+ return UnityFileTools.CompileLegacyPackage(config);
120
+ }
121
+
122
+ private static void ApplyVersion(PackageManifestConfig config, string version)
123
+ {
124
+ if (string.IsNullOrEmpty(version) || config.packageVersion == version)
125
+ return;
126
+
127
+ config.packageVersion = version;
128
+ EditorUtility.SetDirty(config);
129
+ }
130
+
131
+ private static List<PackageManifestConfig> MatchByField(
132
+ PackageManifestConfig[] configs,
133
+ string selector,
134
+ Func<PackageManifestConfig, string> getField)
135
+ {
136
+ return configs
137
+ .Where(x => string.Compare(getField(x), selector, StringComparison.OrdinalIgnoreCase) == 0)
138
+ .ToList();
139
+ }
140
+ }
141
+ }
@@ -0,0 +1,11 @@
1
+ fileFormatVersion: 2
2
+ guid: 0820e17796614bc8932db35ef4afb0ee
3
+ MonoImporter:
4
+ externalObjects: {}
5
+ serializedVersion: 2
6
+ defaultReferences: []
7
+ executionOrder: 0
8
+ icon: {instanceID: 0}
9
+ userData:
10
+ assetBundleName:
11
+ assetBundleVariant: