Skip to content

Commit b5551bd

Browse files
committed
Include Documentation to UPM package
Unity Package Manager expects the offline documentation at "Documentation~/index.md". This change makes sure it is generated through export script. Change-Id: I00af261c3610880b3a90e0bd90598f3f61968b13
1 parent 80629a9 commit b5551bd

File tree

4 files changed

+173
-10
lines changed

4 files changed

+173
-10
lines changed

export_unity_package_config.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@
5353
"readme": "ExternalDependencyManager/Editor/README.md",
5454
"license": "ExternalDependencyManager/Editor/LICENSE",
5555
"changelog": "ExternalDependencyManager/Editor/CHANGELOG.md",
56+
"documentation": "ExternalDependencyManager/Editor/README.md",
5657

5758
"common_manifest" : {
5859
"name": "com.google.external-dependency-manager",

source/ExportUnityPackage/export_unity_package.py

Lines changed: 58 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -537,6 +537,10 @@
537537
# Valid version for asset package in form of major.minor.patch(-preview)
538538
VALID_VERSION_RE = re.compile(r"^[0-9]+\.[0-9]+\.[0-9]+(-preview)?$")
539539

540+
# Documentation folder and filename for UPM package.
541+
UPM_DOCUMENTATION_DIRECTORY = "Documentation~"
542+
UPM_DOCUMENTATION_FILENAME = "index.md"
543+
540544
# String and unicode classes used to check types with safe_dict_get_value()
541545
try:
542546
unicode("") # See whether unicode class is available (Python < 3)
@@ -1008,20 +1012,28 @@ def get_guid(self, path):
10081012

10091013

10101014
def copy_and_set_rwx(source_path, target_path):
1011-
"""Copy a file and set the target file to readable / writeable & executable.
1015+
"""Copy a file/folder and set the target to readable / writeable & executable.
10121016
10131017
Args:
10141018
source_path: File to copy from.
10151019
target_path: Path to copy to.
10161020
"""
10171021
logging.debug("Copying %s --> %s", source_path, target_path)
1018-
target_dir = os.path.dirname(target_path)
1019-
if not os.path.exists(target_dir):
1020-
os.makedirs(target_dir)
1021-
shutil.copy(source_path, target_path)
1022-
os.chmod(target_path, (stat.S_IRWXU | stat.S_IRWXG | stat.S_IROTH |
1023-
stat.S_IXOTH))
1024-
1022+
file_mode = stat.S_IRWXU | stat.S_IRWXG | stat.S_IROTH | stat.S_IXOTH
1023+
1024+
if os.path.isfile(source_path):
1025+
target_dir = os.path.dirname(target_path)
1026+
if not os.path.exists(target_dir):
1027+
os.makedirs(target_dir)
1028+
shutil.copy(source_path, target_path)
1029+
os.chmod(target_path, file_mode)
1030+
elif os.path.isdir(source_path):
1031+
shutil.copytree(source_path, target_path)
1032+
for current_dir, directories, filenames in os.walk(target_path):
1033+
for directory in [os.path.join(current_dir, d) for d in directories]:
1034+
os.chmod(directory, file_mode)
1035+
for filename in [os.path.join(current_dir, f) for f in filenames]:
1036+
os.chmod(filename, file_mode)
10251037

10261038
def version_handler_tag(islabel=True, field=None, value=None):
10271039
"""Generate a VersionHandler filename or label.
@@ -2720,6 +2732,44 @@ def write_upm(self,
27202732
timestamp)
27212733
logging.info("- Processed %s --> %s", asset.filename, asset_file)
27222734

2735+
# Copy documents to "Documentation~" folder.
2736+
# See https://docs.unity3d.com/Manual/cus-layout.html
2737+
# All folders and files does not need meta file or the link on Unity
2738+
# Package Manager would fail.
2739+
source_doc = safe_dict_get_value(self._json, "documentation")
2740+
if source_doc:
2741+
# Try to find the source doc from assets directory
2742+
for assets_dir in assets_dirs:
2743+
source_doc_candidate = os.path.join(assets_dir, source_doc)
2744+
if os.path.exists(source_doc_candidate):
2745+
source_doc = source_doc_candidate
2746+
if os.path.isfile(source_doc):
2747+
# Copy file
2748+
target_doc = os.path.join(staging_dir, "package",
2749+
UPM_DOCUMENTATION_DIRECTORY,
2750+
UPM_DOCUMENTATION_FILENAME)
2751+
logging.info("- Copying doc file %s --> %s", source_doc, target_doc)
2752+
copy_and_set_rwx(source_doc, target_doc)
2753+
elif os.path.isdir(source_doc):
2754+
target_doc_dir = os.path.join(staging_dir, "package",
2755+
UPM_DOCUMENTATION_DIRECTORY)
2756+
# Check if index.md exists
2757+
if not os.path.exists(os.path.join(source_doc,
2758+
UPM_DOCUMENTATION_FILENAME)):
2759+
raise ProjectConfigurationError(
2760+
"Cannot find index.md under '%s' for package '%s'. Perhaps it "
2761+
"is not included in assets_dir or assets_zip?" % (
2762+
source_doc, self.name))
2763+
2764+
logging.info("- Copying doc folder %s --> %s",
2765+
source_doc, target_doc_dir)
2766+
copy_and_set_rwx(source_doc, target_doc_dir)
2767+
else:
2768+
raise ProjectConfigurationError(
2769+
"Cannot find documentation at '%s' for package '%s'. Perhaps the "
2770+
"file/folder is not included in assets_dir or assets_zip?" % (
2771+
from_location, self.name))
2772+
27232773
# Create the .tgz file.
27242774
PackageConfiguration.create_archive(unity_package_file, staging_dir,
27252775
timestamp)

source/ExportUnityPackage/export_unity_package_test.py

Lines changed: 113 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2988,6 +2988,7 @@ def test_package_write_upm(self):
29882988
}],
29892989
"manifest_path": "PlayServicesResolver/Editor",
29902990
"readme": "PlayServicesResolver/Editor/README.md",
2991+
"documentation": "PlayServicesResolver/Doc",
29912992
"includes":
29922993
["ios-resolver.unitypackage", "jar-resolver.unitypackage"],
29932994
"common_manifest": {
@@ -3069,6 +3070,8 @@ def test_package_write_upm(self):
30693070
"Google.VersionHandlerImpl_v1.2.87.0.dll",
30703071
"package/PlayServicesResolver/Editor/"
30713072
"Google.VersionHandlerImpl_v1.2.87.0.dll.meta",
3073+
"package/Documentation~",
3074+
"package/Documentation~/index.md",
30723075
], unitypackage_file.getnames())
30733076
unitypackage_file.extractall(self.staging_dir)
30743077

@@ -3081,6 +3084,14 @@ def test_package_write_upm(self):
30813084
self.staging_dir, "package/PlayServicesResolver/Editor/"
30823085
"Google.VersionHandler.dll")))
30833086

3087+
self.assertTrue(
3088+
filecmp.cmp(
3089+
os.path.join(
3090+
self.assets_dir,
3091+
"PlayServicesResolver/Doc/index.md"),
3092+
os.path.join(
3093+
self.staging_dir, "package/Documentation~/index.md")))
3094+
30843095
# Check package.json
30853096
with open(os.path.join(self.staging_dir, "package/package.json"),
30863097
"rt") as manifest:
@@ -3110,6 +3121,76 @@ def test_package_write_upm(self):
31103121
self.assertEqual(expected_override_metadata,
31113122
yaml_dict["PluginImporter"]["platformData"])
31123123

3124+
def test_package_write_upm_documentation_as_file(self):
3125+
"""Test write_upm() with documentation path as a file."""
3126+
project = export_unity_package.ProjectConfiguration(
3127+
{
3128+
"packages": [{
3129+
"name": "play-services-resolver.unitypackage",
3130+
"imports": [{
3131+
"paths": [
3132+
"PlayServicesResolver/Editor/Google.VersionHandler.dll",
3133+
]
3134+
}],
3135+
"manifest_path": "PlayServicesResolver/Editor",
3136+
# Use README.md as documentation.
3137+
"documentation": "PlayServicesResolver/Editor/README.md",
3138+
"common_manifest": {
3139+
"name": "com.google.play-services-resolver",
3140+
},
3141+
"export_upm": 1
3142+
}]
3143+
}, set(), "1.0.0")
3144+
package = project.packages_by_name["play-services-resolver.unitypackage"]
3145+
3146+
upm_package = package.write_upm(
3147+
export_unity_package.GuidDatabase(
3148+
export_unity_package.DuplicateGuidsChecker(), {
3149+
"1.0.0": {
3150+
"PlayServicesResolver/Editor/README.md":
3151+
"baa27a4c0385454899a759d9852966b7",
3152+
"PlayServicesResolver/Editor/"
3153+
"play-services-resolver_version-1.0.0_manifest.txt":
3154+
"353f6aace2cd42adb1343fc6a808f62e",
3155+
"com.google.play-services-resolver/package.json":
3156+
"782a38c5f19e4bb99e927976c8daa9ac",
3157+
"com.google.play-services-resolver/PlayServicesResolver":
3158+
"fa7daf703ad1430dad0cd8b764e5e6d2",
3159+
"com.google.play-services-resolver/PlayServicesResolver/"
3160+
"Editor":
3161+
"2334cd7684164851a8a53db5bd5923ca",
3162+
}
3163+
}, "1.0.0"), [self.assets_dir], self.staging_dir, 0)
3164+
3165+
with tarfile.open(upm_package, "r:gz") as upm_package_file:
3166+
# Check included files.
3167+
self.assertCountEqual([
3168+
"package",
3169+
"package/package.json",
3170+
"package/package.json.meta",
3171+
"package/PlayServicesResolver",
3172+
"package/PlayServicesResolver.meta",
3173+
"package/PlayServicesResolver/Editor",
3174+
"package/PlayServicesResolver/Editor.meta",
3175+
"package/PlayServicesResolver/Editor/Google.VersionHandler.dll",
3176+
"package/PlayServicesResolver/Editor/Google.VersionHandler.dll.meta",
3177+
"package/PlayServicesResolver/Editor/"
3178+
"play-services-resolver_version-1.0.0_manifest.txt",
3179+
"package/PlayServicesResolver/Editor/"
3180+
"play-services-resolver_version-1.0.0_manifest.txt.meta",
3181+
"package/Documentation~",
3182+
"package/Documentation~/index.md",
3183+
], upm_package_file.getnames())
3184+
upm_package_file.extractall(self.staging_dir)
3185+
3186+
self.assertTrue(
3187+
filecmp.cmp(
3188+
os.path.join(
3189+
self.assets_dir,
3190+
"PlayServicesResolver/Editor/README.md"),
3191+
os.path.join(
3192+
self.staging_dir, "package/Documentation~/index.md")))
3193+
31133194
def test_package_write_upm_missing_readme(self):
31143195
"""Test write_upm() with misconfigured readme path."""
31153196
project = export_unity_package.ProjectConfiguration(
@@ -3204,13 +3285,20 @@ def setUp(self):
32043285
"""Unpack resources to a temporary directory."""
32053286
super(FileOperationsTest, self).setUp()
32063287
self.assets_dir = os.path.join(TEST_DATA_PATH, "Assets")
3288+
self.temp_dir = os.path.join(FLAGS.test_tmpdir, "copy_temp")
3289+
os.makedirs(self.temp_dir)
3290+
3291+
def tearDown(self):
3292+
"""Clean up the temporary directory."""
3293+
super(FileOperationsTest, self).tearDown()
3294+
shutil.rmtree(self.temp_dir)
32073295

32083296
def test_copy_and_set_rwx(self):
32093297
"""Copy a file and set it to readable / writeable and executable."""
32103298
source_path = os.path.join(
32113299
self.assets_dir,
32123300
"PlayServicesResolver/Editor/play-services-resolver_v1.2.87.0.txt")
3213-
target_path = os.path.join(FLAGS.test_tmpdir,
3301+
target_path = os.path.join(self.temp_dir,
32143302
"play-services-resolver_v1.2.87.0.txt")
32153303
self.assertFalse(os.path.exists(target_path))
32163304

@@ -3227,7 +3315,7 @@ def test_copy_and_set_rwx_new_dir(self):
32273315
self.assets_dir,
32283316
"PlayServicesResolver/Editor/play-services-resolver_v1.2.87.0.txt")
32293317
target_path = os.path.join(
3230-
FLAGS.test_tmpdir,
3318+
self.temp_dir,
32313319
"a/nonexistent/directory/play-services-resolver_v1.2.87.0.txt")
32323320
self.assertFalse(os.path.exists(target_path))
32333321

@@ -3262,6 +3350,29 @@ def test_copy_files_to_dir(self):
32623350
finally:
32633351
export_unity_package.copy_and_set_rwx = original_copy_and_set_rwx
32643352

3353+
def test_copy_dir_to_dir(self):
3354+
"""Test copying directory into a directory recursively."""
3355+
source_path = os.path.join(
3356+
self.assets_dir,
3357+
"PlayServicesResolver")
3358+
target_path = os.path.join(
3359+
FLAGS.test_tmpdir,
3360+
"a/nonexistent/directory")
3361+
self.assertFalse(os.path.exists(target_path))
3362+
3363+
export_unity_package.copy_and_set_rwx(source_path, target_path)
3364+
self.assertTrue(os.path.exists(target_path))
3365+
cmp_result = filecmp.dircmp(source_path, target_path)
3366+
self.assertFalse(cmp_result.left_only or cmp_result.right_only or
3367+
cmp_result.diff_files)
3368+
self.assertEqual(
3369+
stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR,
3370+
os.stat(os.path.join(target_path, "Editor")).st_mode & stat.S_IRWXU)
3371+
self.assertEqual(
3372+
stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR,
3373+
(os.stat(os.path.join(target_path, "Editor.meta")).st_mode &
3374+
stat.S_IRWXU))
3375+
32653376

32663377
class ReadJsonFileTest(absltest.TestCase):
32673378
"""Test reading a JSON file."""
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
A documentation file

0 commit comments

Comments
 (0)