diff --git a/platformio/builder/tools/piolib.py b/platformio/builder/tools/piolib.py index 234a621a..69d645c5 100644 --- a/platformio/builder/tools/piolib.py +++ b/platformio/builder/tools/piolib.py @@ -17,7 +17,6 @@ from __future__ import absolute_import -import codecs import hashlib import os import re @@ -34,6 +33,7 @@ from platformio import exception, fs, util from platformio.builder.tools import platformio as piotool from platformio.compat import WINDOWS, hashlib_encode_data, string_types from platformio.managers.lib import LibraryManager +from platformio.package.manifest.parser import ManifestParserFactory class LibBuilderFactory(object): @@ -456,17 +456,10 @@ class UnknownLibBuilder(LibBuilderBase): class ArduinoLibBuilder(LibBuilderBase): def load_manifest(self): - manifest = {} - if not isfile(join(self.path, "library.properties")): - return manifest manifest_path = join(self.path, "library.properties") - with codecs.open(manifest_path, encoding="utf-8") as fp: - for line in fp.readlines(): - if "=" not in line: - continue - key, value = line.split("=", 1) - manifest[key.strip()] = value.strip() - return manifest + if not isfile(manifest_path): + return {} + return ManifestParserFactory.new_from_file(manifest_path).as_dict() def get_include_dirs(self): include_dirs = LibBuilderBase.get_include_dirs(self) @@ -510,24 +503,7 @@ class ArduinoLibBuilder(LibBuilderBase): return util.items_in_list(frameworks, ["arduino", "energia"]) def is_platforms_compatible(self, platforms): - platforms_map = { - "avr": ["atmelavr"], - "sam": ["atmelsam"], - "samd": ["atmelsam"], - "esp8266": ["espressif8266"], - "esp32": ["espressif32"], - "arc32": ["intel_arc32"], - "stm32": ["ststm32"], - "nrf5": ["nordicnrf51", "nordicnrf52"], - } - items = [] - for arch in self._manifest.get("architectures", "").split(","): - arch = arch.strip().lower() - if arch == "*": - items = "*" - break - if arch in platforms_map: - items.extend(platforms_map[arch]) + items = self._manifest.get("platforms", []) if not items: return LibBuilderBase.is_platforms_compatible(self, platforms) return util.items_in_list(platforms, items) @@ -535,9 +511,10 @@ class ArduinoLibBuilder(LibBuilderBase): class MbedLibBuilder(LibBuilderBase): def load_manifest(self): - if not isfile(join(self.path, "module.json")): + manifest_path = join(self.path, "module.json") + if not isfile(manifest_path): return {} - return fs.load_json(join(self.path, "module.json")) + return ManifestParserFactory.new_from_file(manifest_path).as_dict() @property def include_dir(self): @@ -682,20 +659,12 @@ class MbedLibBuilder(LibBuilderBase): class PlatformIOLibBuilder(LibBuilderBase): def load_manifest(self): - assert isfile(join(self.path, "library.json")) - manifest = fs.load_json(join(self.path, "library.json")) - assert "name" in manifest + manifest_path = join(self.path, "library.json") + if not isfile(manifest_path): + return {} + return ManifestParserFactory.new_from_file(manifest_path).as_dict() - # replace "espressif" old name dev/platform with ESP8266 - if "platforms" in manifest: - manifest["platforms"] = [ - "espressif8266" if p == "espressif" else p - for p in util.items_to_list(manifest["platforms"]) - ] - - return manifest - - def _is_arduino_manifest(self): + def _has_arduino_manifest(self): return isfile(join(self.path, "library.properties")) @property @@ -718,7 +687,7 @@ class PlatformIOLibBuilder(LibBuilderBase): return self._manifest.get("build").get("srcFilter") if self.env["SRC_FILTER"]: return self.env["SRC_FILTER"] - if self._is_arduino_manifest(): + if self._has_arduino_manifest(): return ArduinoLibBuilder.src_filter.fget(self) return LibBuilderBase.src_filter.fget(self) @@ -789,7 +758,7 @@ class PlatformIOLibBuilder(LibBuilderBase): # backwards compatibility with PlatformIO 2.0 if ( "build" not in self._manifest - and self._is_arduino_manifest() + and self._has_arduino_manifest() and not isdir(join(self.path, "src")) and isdir(join(self.path, "utility")) ): @@ -954,9 +923,8 @@ def IsCompatibleLibBuilder(env, lb, verbose=int(ARGUMENTS.get("PIOVERBOSE", 0))) if verbose: sys.stderr.write("Platform incompatible library %s\n" % lb.path) return False - if ( - compat_mode in ("soft", "strict") - and not lb.is_frameworks_compatible(env.get("PIOFRAMEWORK", [])) + if compat_mode in ("soft", "strict") and not lb.is_frameworks_compatible( + env.get("PIOFRAMEWORK", []) ): if verbose: sys.stderr.write("Framework incompatible library %s\n" % lb.path) diff --git a/platformio/managers/lib.py b/platformio/managers/lib.py index 8a120c3a..e85a1225 100644 --- a/platformio/managers/lib.py +++ b/platformio/managers/lib.py @@ -16,7 +16,6 @@ # pylint: disable=too-many-return-statements import json -import re from glob import glob from os.path import isdir, join @@ -62,73 +61,6 @@ class LibraryManager(BasePkgManager): return None - def load_manifest(self, pkg_dir): - manifest = BasePkgManager.load_manifest(self, pkg_dir) - if not manifest: - return manifest - - # if Arduino library.properties - if "sentence" in manifest: - manifest["frameworks"] = ["arduino"] - manifest["description"] = manifest["sentence"] - del manifest["sentence"] - - if "author" in manifest: - if isinstance(manifest["author"], dict): - manifest["authors"] = [manifest["author"]] - else: - manifest["authors"] = [{"name": manifest["author"]}] - del manifest["author"] - - if "authors" in manifest and not isinstance(manifest["authors"], list): - manifest["authors"] = [manifest["authors"]] - - if "keywords" not in manifest: - keywords = [] - for keyword in re.split( - r"[\s/]+", manifest.get("category", "Uncategorized") - ): - keyword = keyword.strip() - if not keyword: - continue - keywords.append(keyword.lower()) - manifest["keywords"] = keywords - if "category" in manifest: - del manifest["category"] - - # don't replace VCS URL - if "url" in manifest and "description" in manifest: - manifest["homepage"] = manifest["url"] - del manifest["url"] - - if "architectures" in manifest: - platforms = [] - platforms_map = { - "avr": "atmelavr", - "sam": "atmelsam", - "samd": "atmelsam", - "esp8266": "espressif8266", - "esp32": "espressif32", - "arc32": "intel_arc32", - } - for arch in manifest["architectures"].split(","): - arch = arch.strip() - if arch == "*": - platforms = "*" - break - if arch in platforms_map: - platforms.append(platforms_map[arch]) - manifest["platforms"] = platforms - del manifest["architectures"] - - # convert listed items via comma to array - for key in ("keywords", "frameworks", "platforms"): - if key not in manifest or not isinstance(manifest[key], string_types): - continue - manifest[key] = [i.strip() for i in manifest[key].split(",") if i.strip()] - - return manifest - @staticmethod def normalize_dependencies(dependencies): if not dependencies: diff --git a/platformio/managers/package.py b/platformio/managers/package.py index f940767b..ba7fb494 100644 --- a/platformio/managers/package.py +++ b/platformio/managers/package.py @@ -12,7 +12,6 @@ # See the License for the specific language governing permissions and # limitations under the License. -import codecs import hashlib import json import os @@ -29,6 +28,10 @@ from platformio import __version__, app, exception, fs, telemetry, util from platformio.compat import hashlib_encode_data from platformio.downloader import FileDownloader from platformio.lockfile import LockFile +from platformio.package.manifest.parser import ( + ManifestParserError, + ManifestParserFactory, +) from platformio.unpacker import FileUnpacker from platformio.vcsclient import VCSClientFactory @@ -326,7 +329,7 @@ class PkgInstallerMixin(object): def manifest_exists(self, pkg_dir): return self.get_manifest_path(pkg_dir) or self.get_src_manifest_path(pkg_dir) - def load_manifest(self, pkg_dir): + def load_manifest(self, pkg_dir): # pylint: disable=too-many-branches cache_key = "load_manifest-%s" % pkg_dir result = self.cache_get(cache_key) if result: @@ -342,15 +345,10 @@ class PkgInstallerMixin(object): if not manifest_path and not src_manifest_path: return None - if manifest_path and manifest_path.endswith(".json"): - manifest = fs.load_json(manifest_path) - elif manifest_path and manifest_path.endswith(".properties"): - with codecs.open(manifest_path, encoding="utf-8") as fp: - for line in fp.readlines(): - if "=" not in line: - continue - key, value = line.split("=", 1) - manifest[key.strip()] = value.strip() + try: + manifest = ManifestParserFactory.new_from_file(manifest_path).as_dict() + except ManifestParserError: + pass if src_manifest: if "version" in src_manifest: diff --git a/platformio/package/manifest/parser.py b/platformio/package/manifest/parser.py index dc7265b3..fb6f06db 100644 --- a/platformio/package/manifest/parser.py +++ b/platformio/package/manifest/parser.py @@ -46,6 +46,8 @@ class ManifestFileType(object): return ManifestFileType.MODULE_JSON if uri.endswith("package.json"): return ManifestFileType.PACKAGE_JSON + if uri.endswith("library.json"): + return ManifestFileType.LIBRARY_JSON return None @@ -331,18 +333,16 @@ class LibraryJsonManifestParser(BaseManifestParser): class ModuleJsonManifestParser(BaseManifestParser): def parse(self, contents): data = json.loads(contents) - return dict( - name=data["name"], - version=data["version"], - keywords=data.get("keywords"), - description=data["description"], - frameworks=["mbed"], - platforms=["*"], - homepage=data.get("homepage"), - export={"exclude": ["tests", "test", "*.doxyfile", "*.pdf"]}, - authors=self._parse_authors(data.get("author")), - license=self._parse_license(data.get("licenses")), - ) + data["frameworks"] = ["mbed"] + data["platforms"] = ["*"] + data["export"] = {"exclude": ["tests", "test", "*.doxyfile", "*.pdf"]} + if "author" in data: + data["authors"] = self._parse_authors(data.get("author")) + del data["author"] + if "licenses" in data: + data["license"] = self._parse_license(data.get("licenses")) + del data["licenses"] + return data def _parse_authors(self, raw): if not raw: @@ -364,23 +364,26 @@ class ModuleJsonManifestParser(BaseManifestParser): class LibraryPropertiesManifestParser(BaseManifestParser): def parse(self, contents): - properties = self._parse_properties(contents) - repository = self._parse_repository(properties) - homepage = properties.get("url") + data = self._parse_properties(contents) + repository = self._parse_repository(data) + homepage = data.get("url") if repository and repository["url"] == homepage: homepage = None - return dict( - frameworks=["arduino"], - homepage=homepage, - repository=repository or None, - name=properties.get("name"), - version=properties.get("version"), - description=self._parse_description(properties), - platforms=self._parse_platforms(properties) or ["*"], - keywords=self._parse_keywords(properties), - authors=self._parse_authors(properties) or None, - export=self._parse_export(), + data.update( + dict( + frameworks=["arduino"], + homepage=homepage, + repository=repository or None, + description=self._parse_description(data), + platforms=self._parse_platforms(data) or ["*"], + keywords=self._parse_keywords(data), + export=self._parse_export(), + ) ) + if "author" in data: + data["authors"] = self._parse_authors(data) + del data["author"] + return data @staticmethod def _parse_properties(contents): diff --git a/tests/test_pkgmanifest.py b/tests/test_pkgmanifest.py index 212793a1..f779b488 100644 --- a/tests/test_pkgmanifest.py +++ b/tests/test_pkgmanifest.py @@ -31,7 +31,11 @@ def test_library_json_parser(): "platforms": ["atmelavr", "espressif"], "url": "http://old.url.format", "exclude": [".gitignore", "tests"], - "include": "mylib" + "include": "mylib", + "build": { + "flags": ["-DHELLO"] + }, + "customField": "Custom Value" } """ mp = parser.LibraryJsonManifestParser(contents) @@ -43,6 +47,8 @@ def test_library_json_parser(): "export": {"exclude": [".gitignore", "tests"], "include": ["mylib"]}, "keywords": ["kw1", "kw2", "kw3"], "homepage": "http://old.url.format", + "build": {"flags": ["-DHELLO"]}, + "customField": "Custom Value", }, ) @@ -89,9 +95,11 @@ def test_module_json_parser(): "type": "git", "url": "git@github.com:username/repo.git" }, - "version": "1.2.3" + "version": "1.2.3", + "customField": "Custom Value" } """ + mp = parser.ModuleJsonManifestParser(contents) assert not jsondiff.diff( mp.as_dict(), @@ -106,6 +114,8 @@ def test_module_json_parser(): "export": {"exclude": ["tests", "test", "*.doxyfile", "*.pdf"]}, "authors": [{"email": "name@surname.com", "name": "Name Surname"}], "version": "1.2.3", + "repository": {"type": "git", "url": "git@github.com:username/repo.git"}, + "customField": "Custom Value", }, ) @@ -117,6 +127,7 @@ name=TestPackage version=1.2.3 author=SomeAuthor sentence=This is Arduino library +customField=Custom Value """ mp = parser.LibraryPropertiesManifestParser(contents) assert not jsondiff.diff( @@ -125,6 +136,7 @@ sentence=This is Arduino library "name": "TestPackage", "version": "1.2.3", "description": "This is Arduino library", + "sentence": "This is Arduino library", "platforms": ["*"], "frameworks": ["arduino"], "export": { @@ -132,6 +144,7 @@ sentence=This is Arduino library }, "authors": [{"email": "info@author.com", "name": "SomeAuthor"}], "keywords": ["uncategorized"], + "customField": "Custom Value", }, )