diff --git a/Cargo.toml b/Cargo.toml index e58deed..809cf99 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "qpm_package" -version = "0.4.0" -edition = "2021" +version = "2.0.0" +edition = "2024" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/README.md b/README.md index 77e36dd..19af220 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,7 @@ # QPM.Package QMOD Package library + +# Generate JSON schemas +``` +cargo run +``` \ No newline at end of file diff --git a/qpm.schema.json b/qpm.schema.json index cb75fbc..0bb0e6c 100644 --- a/qpm.schema.json +++ b/qpm.schema.json @@ -4,47 +4,78 @@ "description": "Configuration for a package.", "type": "object", "required": [ - "dependencies", - "dependenciesDir", - "info", - "sharedDir" + "dependenciesDirectory", + "id", + "sharedDirectory", + "triplets", + "version" ], "properties": { - "dependencies": { - "description": "The dependencies of the package.", - "type": "array", - "items": { - "$ref": "#/definitions/PackageDependency" - } + "additionalData": { + "description": "Additional package metadata", + "default": { + "author": "", + "description": "", + "license": "" + }, + "allOf": [ + { + "$ref": "#/definitions/PackageAdditionalData" + } + ] }, - "dependenciesDir": { - "description": "The directory where dependencies are stored.", + "cmake": { + "description": "Whether to generate CMake files on restore.", + "type": [ + "boolean", + "null" + ] + }, + "configVersion": { + "description": "Config version, defaults to 2.0.0", + "default": "2.0.0", + "type": "string", + "pattern": "^(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(?:-((?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\.(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\\+([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?$" + }, + "dependenciesDirectory": { + "description": "Directory where dependencies are restored", "type": "string" }, - "info": { - "description": "The package metadata.", + "id": { + "description": "Package ID", "allOf": [ { - "$ref": "#/definitions/PackageMetadata" + "$ref": "#/definitions/DependencyId" } ] }, - "sharedDir": { - "description": "The directory where shared files are stored.", + "sharedDirectory": { + "description": "Directories shared by the package", "type": "string" }, + "toolchainOut": { + "description": "Path to generate a toolchain JSON file describing the project setup configuration.", + "type": [ + "string", + "null" + ] + }, + "triplets": { + "description": "Package triplet configurations", + "allOf": [ + { + "$ref": "#/definitions/PackageTripletsConfig" + } + ] + }, "version": { - "description": "The version of the package configuration.", - "default": "0.4.0", + "description": "Package version", "type": "string", "pattern": "^(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(?:-((?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\.(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\\+([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?$" }, "workspace": { - "description": "The workspace configuration.", + "description": "Workspace configuration", "default": { - "qmodIncludeDirs": [], - "qmodIncludeFiles": [], - "qmodOutput": null, "scripts": {} }, "allOf": [ @@ -55,101 +86,130 @@ } }, "definitions": { - "AdditionalPackageMetadata": { - "description": "Additional metadata for the package.", + "DependencyId": { + "type": "string" + }, + "PackageAdditionalData": { "type": "object", "properties": { - "branchName": { - "description": "The branch name of a GitHub repository. Only used when a valid GitHub URL is provided.", - "type": [ - "string", - "null" - ] + "author": { + "description": "Package author", + "default": "", + "type": "string" }, - "cmake": { - "description": "Whether to generate CMake files on restore.", - "type": [ - "boolean", - "null" - ] + "description": { + "description": "Package description", + "default": "", + "type": "string" }, + "license": { + "description": "Package license", + "default": "", + "type": "string" + } + } + }, + "PackageTriplet": { + "description": "Configuration for a package triplet.", + "type": "object", + "required": [ + "ndk" + ], + "properties": { "compileOptions": { "description": "Additional compile options for the package.", "anyOf": [ { - "$ref": "#/definitions/CompileOptions" + "$ref": "#/definitions/PackageTripletCompileOptions" }, { "type": "null" } ] }, - "debugSoLink": { - "description": "The link to the debug shared object file.", - "type": [ - "string", - "null" - ] + "dependencies": { + "description": "Dependencies for this triplet", + "default": {}, + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/PackageTripletDependency" + } }, - "headersOnly": { - "description": "Whether or not the package is header only", - "type": [ - "boolean", - "null" - ] + "devDependencies": { + "description": "Dependencies for this triplet", + "default": {}, + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/PackageTripletDependency" + } }, - "modLink": { - "description": "The link to the qmod file.", - "type": [ - "string", - "null" - ] + "env": { + "description": "Environment variables for this triplet.", + "default": {}, + "type": "object", + "additionalProperties": { + "type": "string" + } }, - "overrideSoName": { - "description": "The override name for the shared object file.", - "type": [ - "string", - "null" - ] + "ndk": { + "description": "The NDK version range.", + "type": "string", + "properties": { + "format": { + "title": "String", + "type": "string" + } + } }, - "overrideStaticName": { - "description": "The override name for the static library file.", + "outBinaries": { + "description": "Output binaries for this triplet.", "type": [ - "string", + "array", "null" - ] + ], + "items": { + "type": "string" + } }, - "soLink": { - "description": "The link to the shared object file.", + "qmodId": { + "description": "QMod ID for this triplet.", "type": [ "string", "null" ] }, - "staticLink": { - "description": "The link to the static library file.", - "type": [ - "string", - "null" - ] + "qmodIncludeDirs": { + "description": "List of directories to search during qmod creation.", + "default": [], + "type": "array", + "items": { + "type": "string" + } + }, + "qmodIncludeFiles": { + "description": "List of files to include in the resulting qmod.", + "default": [], + "type": "array", + "items": { + "type": "string" + } }, - "staticLinking": { - "description": "Whether the package is statically linked. Deprecated, use staticLink instead.", - "deprecated": true, + "qmodOutput": { + "description": "Output path for the qmod.", "type": [ - "boolean", + "string", "null" ] }, - "subFolder": { - "description": "Sub-folder to use from the downloaded repository or zip, so one repository can contain multiple packages.", + "qmodTemplate": { + "description": "QMod template for this triplet.", "type": [ "string", "null" ] }, - "toolchainOut": { - "description": "Path to generate a toolchain JSON file describing the project setup configuration.", + "qmodUrl": { + "description": "QMod URL for this triplet.", "type": [ "string", "null" @@ -157,7 +217,7 @@ } } }, - "CompileOptions": { + "PackageTripletCompileOptions": { "description": "Additional options for compilation and edits to compilation related files.", "type": "object", "properties": { @@ -171,17 +231,6 @@ "type": "string" } }, - "cppFeatures": { - "description": "Additional C++ features to add. Deprecated, unused and exclusive to CMake.", - "deprecated": true, - "type": [ - "array", - "null" - ], - "items": { - "type": "string" - } - }, "cppFlags": { "description": "Additional C++ flags to add.", "type": [ @@ -193,7 +242,7 @@ } }, "includePaths": { - "description": "Additional include paths to add, relative to the extern directory.", + "description": "Additional include paths to add, relative to the extern package's shared directory.", "type": [ "array", "null" @@ -203,7 +252,7 @@ } }, "systemIncludes": { - "description": "Additional system include paths to add, relative to the extern directory.", + "description": "Additional system include paths to add, relative to the extern package's shared directory.", "type": [ "array", "null" @@ -214,55 +263,38 @@ } } }, - "DependencyLibType": { - "description": "Describes the dependency type.", - "oneOf": [ - { - "description": "Shared library", - "type": "string", - "enum": [ - "shared" - ] - }, - { - "description": "Static library", - "type": "string", - "enum": [ - "static" - ] - }, - { - "description": "Header only", - "type": "string", - "enum": [ - "headerOnly" - ] - } - ] - }, - "PackageDependency": { - "description": "A dependency of the package.", + "PackageTripletDependency": { + "description": "Dependency information for a package triplet.", "type": "object", "required": [ - "additionalData", - "id", "versionRange" ], "properties": { - "additionalData": { - "description": "Additional metadata for the dependency", - "allOf": [ + "qmodExport": { + "description": "Whether to include this dependency in the qmod", + "default": false, + "type": "boolean" + }, + "qmodRequired": { + "description": "QMod required field for this dependency.", + "type": [ + "boolean", + "null" + ] + }, + "triplet": { + "description": "Target triplet for this dependency.", + "anyOf": [ { - "$ref": "#/definitions/PackageDependencyModifier" + "$ref": "#/definitions/TripletId" + }, + { + "type": "null" } ] }, - "id": { - "description": "The unique identifier of the dependency", - "type": "string" - }, "versionRange": { - "description": "The version range of the dependency", + "description": "Version range requirement", "type": "string", "properties": { "format": { @@ -273,142 +305,41 @@ } } }, - "PackageDependencyModifier": { - "description": "Modifies how a dependency should be restored.", + "PackageTripletsConfig": { + "description": "Configuration for a package's triplets map", "type": "object", "properties": { - "extraFiles": { - "description": "Additional files to be downloaded.", - "type": [ - "array", - "null" - ], - "items": { - "type": "string" - } - }, - "includeQmod": { - "description": "If the mod dependency should be included in the generated mod.json. Defaults to true.", - "type": [ - "boolean", - "null" - ] - }, - "libType": { - "description": "Specifies how to restore this dependency.", + "base": { + "description": "Default configuration for all triplets. All triplets will inherit from this.", "anyOf": [ { - "$ref": "#/definitions/DependencyLibType" + "$ref": "#/definitions/PackageTriplet" }, { "type": "null" } ] }, - "localPath": { - "description": "Copy a dependency from a location that is local to this root path instead of from a remote URL.", - "type": [ - "string", - "null" - ] - }, - "private": { - "description": "Whether or not the dependency is private and should be used in restore.", - "type": [ - "boolean", - "null" - ] - }, - "required": { - "description": "Whether the mod is optional or required. If omitted, assume true.", - "type": [ - "boolean", - "null" - ] - } - } - }, - "PackageMetadata": { - "description": "Metadata information about the package.", - "type": "object", - "required": [ - "additionalData", - "id", - "name", - "version" - ], - "properties": { - "additionalData": { - "description": "Additional metadata for the package.", - "allOf": [ + "default": { + "description": "Default triplet ID for this package.", + "anyOf": [ + { + "$ref": "#/definitions/TripletId" + }, { - "$ref": "#/definitions/AdditionalPackageMetadata" + "type": "null" } ] - }, - "id": { - "description": "The unique identifier of the package.", - "type": "string" - }, - "name": { - "description": "The name of the package.", - "type": "string" - }, - "url": { - "description": "The website for the package.", - "type": [ - "string", - "null" - ] - }, - "version": { - "description": "The version of the package.", - "type": "string", - "pattern": "^(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(?:-((?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\.(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\\+([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?$" } } }, + "TripletId": { + "type": "string" + }, "WorkspaceConfig": { "description": "Configuration for the workspace.", "type": "object", - "required": [ - "ndk" - ], "properties": { - "ndk": { - "description": "The NDK version range.", - "type": "string", - "properties": { - "format": { - "title": "String", - "type": "string" - } - } - }, - "qmodIncludeDirs": { - "description": "List of directories to search during qmod creation.", - "default": [], - "type": "array", - "items": { - "type": "string" - } - }, - "qmodIncludeFiles": { - "description": "List of files to include in the resulting qmod.", - "default": [], - "type": "array", - "items": { - "type": "string" - } - }, - "qmodOutput": { - "description": "Output path for the qmod.", - "default": null, - "type": [ - "string", - "null" - ] - }, "scripts": { "description": "Scripts associated with the workspace.", "default": {}, diff --git a/qpm.shared.schema.json b/qpm.shared.schema.json index 25c4df7..97cde99 100644 --- a/qpm.shared.schema.json +++ b/qpm.shared.schema.json @@ -1,39 +1,79 @@ { "$schema": "http://json-schema.org/draft-07/schema#", "title": "SharedPackageConfig", - "description": "Shared package configuration.", + "description": "Configuration for a shared package.", "type": "object", "required": [ "config", - "restoredDependencies" + "lockedTriplet", + "restoredTriplet" ], "properties": { "config": { - "description": "A copy of the package configuration stored in qpm.json for convenience.", + "description": "Package name", "allOf": [ { "$ref": "#/definitions/PackageConfig" } ] }, - "restoredDependencies": { - "description": "The resolved dependencies of the package.", - "type": "array", - "items": { - "$ref": "#/definitions/SharedDependency" + "lockedTriplet": { + "description": "Triplet map", + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/SharedTriplet" } + }, + "restoredTriplet": { + "$ref": "#/definitions/TripletId" } }, "definitions": { - "AdditionalPackageMetadata": { - "description": "Additional metadata for the package.", + "DependencyId": { + "type": "string" + }, + "PackageAdditionalData": { "type": "object", "properties": { - "branchName": { - "description": "The branch name of a GitHub repository. Only used when a valid GitHub URL is provided.", - "type": [ - "string", - "null" + "author": { + "description": "Package author", + "default": "", + "type": "string" + }, + "description": { + "description": "Package description", + "default": "", + "type": "string" + }, + "license": { + "description": "Package license", + "default": "", + "type": "string" + } + } + }, + "PackageConfig": { + "description": "Configuration for a package.", + "type": "object", + "required": [ + "dependenciesDirectory", + "id", + "sharedDirectory", + "triplets", + "version" + ], + "properties": { + "additionalData": { + "description": "Additional package metadata", + "default": { + "author": "", + "description": "", + "license": "" + }, + "allOf": [ + { + "$ref": "#/definitions/PackageAdditionalData" + } ] }, "cmake": { @@ -43,83 +83,162 @@ "null" ] }, - "compileOptions": { - "description": "Additional compile options for the package.", - "anyOf": [ - { - "$ref": "#/definitions/CompileOptions" - }, + "configVersion": { + "description": "Config version, defaults to 2.0.0", + "default": "2.0.0", + "type": "string", + "pattern": "^(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(?:-((?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\.(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\\+([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?$" + }, + "dependenciesDirectory": { + "description": "Directory where dependencies are restored", + "type": "string" + }, + "id": { + "description": "Package ID", + "allOf": [ { - "type": "null" + "$ref": "#/definitions/DependencyId" } ] }, - "debugSoLink": { - "description": "The link to the debug shared object file.", + "sharedDirectory": { + "description": "Directories shared by the package", + "type": "string" + }, + "toolchainOut": { + "description": "Path to generate a toolchain JSON file describing the project setup configuration.", "type": [ "string", "null" ] }, - "headersOnly": { - "description": "Whether or not the package is header only", - "type": [ - "boolean", - "null" + "triplets": { + "description": "Package triplet configurations", + "allOf": [ + { + "$ref": "#/definitions/PackageTripletsConfig" + } ] }, - "modLink": { - "description": "The link to the qmod file.", - "type": [ - "string", - "null" - ] + "version": { + "description": "Package version", + "type": "string", + "pattern": "^(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(?:-((?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\.(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\\+([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?$" }, - "overrideSoName": { - "description": "The override name for the shared object file.", - "type": [ - "string", - "null" + "workspace": { + "description": "Workspace configuration", + "default": { + "scripts": {} + }, + "allOf": [ + { + "$ref": "#/definitions/WorkspaceConfig" + } ] - }, - "overrideStaticName": { - "description": "The override name for the static library file.", - "type": [ - "string", - "null" + } + } + }, + "PackageTriplet": { + "description": "Configuration for a package triplet.", + "type": "object", + "required": [ + "ndk" + ], + "properties": { + "compileOptions": { + "description": "Additional compile options for the package.", + "anyOf": [ + { + "$ref": "#/definitions/PackageTripletCompileOptions" + }, + { + "type": "null" + } ] }, - "soLink": { - "description": "The link to the shared object file.", + "dependencies": { + "description": "Dependencies for this triplet", + "default": {}, + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/PackageTripletDependency" + } + }, + "devDependencies": { + "description": "Dependencies for this triplet", + "default": {}, + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/PackageTripletDependency" + } + }, + "env": { + "description": "Environment variables for this triplet.", + "default": {}, + "type": "object", + "additionalProperties": { + "type": "string" + } + }, + "ndk": { + "description": "The NDK version range.", + "type": "string", + "properties": { + "format": { + "title": "String", + "type": "string" + } + } + }, + "outBinaries": { + "description": "Output binaries for this triplet.", "type": [ - "string", + "array", "null" - ] + ], + "items": { + "type": "string" + } }, - "staticLink": { - "description": "The link to the static library file.", + "qmodId": { + "description": "QMod ID for this triplet.", "type": [ "string", "null" ] }, - "staticLinking": { - "description": "Whether the package is statically linked. Deprecated, use staticLink instead.", - "deprecated": true, + "qmodIncludeDirs": { + "description": "List of directories to search during qmod creation.", + "default": [], + "type": "array", + "items": { + "type": "string" + } + }, + "qmodIncludeFiles": { + "description": "List of files to include in the resulting qmod.", + "default": [], + "type": "array", + "items": { + "type": "string" + } + }, + "qmodOutput": { + "description": "Output path for the qmod.", "type": [ - "boolean", + "string", "null" ] }, - "subFolder": { - "description": "Sub-folder to use from the downloaded repository or zip, so one repository can contain multiple packages.", + "qmodTemplate": { + "description": "QMod template for this triplet.", "type": [ "string", "null" ] }, - "toolchainOut": { - "description": "Path to generate a toolchain JSON file describing the project setup configuration.", + "qmodUrl": { + "description": "QMod URL for this triplet.", "type": [ "string", "null" @@ -127,7 +246,7 @@ } } }, - "CompileOptions": { + "PackageTripletCompileOptions": { "description": "Additional options for compilation and edits to compilation related files.", "type": "object", "properties": { @@ -141,17 +260,6 @@ "type": "string" } }, - "cppFeatures": { - "description": "Additional C++ features to add. Deprecated, unused and exclusive to CMake.", - "deprecated": true, - "type": [ - "array", - "null" - ], - "items": { - "type": "string" - } - }, "cppFlags": { "description": "Additional C++ flags to add.", "type": [ @@ -163,7 +271,7 @@ } }, "includePaths": { - "description": "Additional include paths to add, relative to the extern directory.", + "description": "Additional include paths to add, relative to the extern package's shared directory.", "type": [ "array", "null" @@ -173,7 +281,7 @@ } }, "systemIncludes": { - "description": "Additional system include paths to add, relative to the extern directory.", + "description": "Additional system include paths to add, relative to the extern package's shared directory.", "type": [ "array", "null" @@ -184,143 +292,38 @@ } } }, - "Dependency": { - "description": "A dependency of the package.", + "PackageTripletDependency": { + "description": "Dependency information for a package triplet.", "type": "object", "required": [ - "additionalData", - "id", "versionRange" ], "properties": { - "additionalData": { - "description": "Additional metadata for the dependency. Deprecated, use packageConfig.additionalData instead.", - "deprecated": true, - "allOf": [ - { - "$ref": "#/definitions/AdditionalPackageMetadata" - } - ] - }, - "id": { - "type": "string" - }, - "versionRange": { - "description": "The version range of the dependency", - "type": "string", - "properties": { - "format": { - "title": "String", - "type": "string" - } - } - } - } - }, - "DependencyLibType": { - "description": "Describes the dependency type.", - "oneOf": [ - { - "description": "Shared library", - "type": "string", - "enum": [ - "shared" - ] - }, - { - "description": "Static library", - "type": "string", - "enum": [ - "static" - ] - }, - { - "description": "Header only", - "type": "string", - "enum": [ - "headerOnly" - ] - } - ] - }, - "PackageConfig": { - "description": "Configuration for a package.", - "type": "object", - "required": [ - "dependencies", - "dependenciesDir", - "info", - "sharedDir" - ], - "properties": { - "dependencies": { - "description": "The dependencies of the package.", - "type": "array", - "items": { - "$ref": "#/definitions/PackageDependency" - } + "qmodExport": { + "description": "Whether to include this dependency in the qmod", + "default": false, + "type": "boolean" }, - "dependenciesDir": { - "description": "The directory where dependencies are stored.", - "type": "string" - }, - "info": { - "description": "The package metadata.", - "allOf": [ - { - "$ref": "#/definitions/PackageMetadata" - } + "qmodRequired": { + "description": "QMod required field for this dependency.", + "type": [ + "boolean", + "null" ] }, - "sharedDir": { - "description": "The directory where shared files are stored.", - "type": "string" - }, - "version": { - "description": "The version of the package configuration.", - "default": "0.4.0", - "type": "string", - "pattern": "^(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(?:-((?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\.(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\\+([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?$" - }, - "workspace": { - "description": "The workspace configuration.", - "default": { - "qmodIncludeDirs": [], - "qmodIncludeFiles": [], - "qmodOutput": null, - "scripts": {} - }, - "allOf": [ + "triplet": { + "description": "Target triplet for this dependency.", + "anyOf": [ { - "$ref": "#/definitions/WorkspaceConfig" - } - ] - } - } - }, - "PackageDependency": { - "description": "A dependency of the package.", - "type": "object", - "required": [ - "additionalData", - "id", - "versionRange" - ], - "properties": { - "additionalData": { - "description": "Additional metadata for the dependency", - "allOf": [ + "$ref": "#/definitions/TripletId" + }, { - "$ref": "#/definitions/PackageDependencyModifier" + "type": "null" } ] }, - "id": { - "description": "The unique identifier of the dependency", - "type": "string" - }, "versionRange": { - "description": "The version range of the dependency", + "description": "Version range requirement", "type": "string", "properties": { "format": { @@ -331,170 +334,104 @@ } } }, - "PackageDependencyModifier": { - "description": "Modifies how a dependency should be restored.", + "PackageTripletsConfig": { + "description": "Configuration for a package's triplets map", "type": "object", "properties": { - "extraFiles": { - "description": "Additional files to be downloaded.", - "type": [ - "array", - "null" - ], - "items": { - "type": "string" - } - }, - "includeQmod": { - "description": "If the mod dependency should be included in the generated mod.json. Defaults to true.", - "type": [ - "boolean", - "null" - ] - }, - "libType": { - "description": "Specifies how to restore this dependency.", + "base": { + "description": "Default configuration for all triplets. All triplets will inherit from this.", "anyOf": [ { - "$ref": "#/definitions/DependencyLibType" + "$ref": "#/definitions/PackageTriplet" }, { "type": "null" } ] }, - "localPath": { - "description": "Copy a dependency from a location that is local to this root path instead of from a remote URL.", - "type": [ - "string", - "null" - ] - }, - "private": { - "description": "Whether or not the dependency is private and should be used in restore.", - "type": [ - "boolean", - "null" - ] - }, - "required": { - "description": "Whether the mod is optional or required. If omitted, assume true.", - "type": [ - "boolean", - "null" - ] - } - } - }, - "PackageMetadata": { - "description": "Metadata information about the package.", - "type": "object", - "required": [ - "additionalData", - "id", - "name", - "version" - ], - "properties": { - "additionalData": { - "description": "Additional metadata for the package.", - "allOf": [ + "default": { + "description": "Default triplet ID for this package.", + "anyOf": [ + { + "$ref": "#/definitions/TripletId" + }, { - "$ref": "#/definitions/AdditionalPackageMetadata" + "type": "null" } ] - }, - "id": { - "description": "The unique identifier of the package.", - "type": "string" - }, - "name": { - "description": "The name of the package.", - "type": "string" - }, - "url": { - "description": "The website for the package.", - "type": [ - "string", - "null" - ] - }, - "version": { - "description": "The version of the package.", - "type": "string", - "pattern": "^(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(?:-((?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\.(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\\+([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?$" } } }, - "SharedDependency": { - "description": "A resolved dependency of the package.", + "SharedTriplet": { + "description": "Configuration for a shared triplet.", "type": "object", "required": [ - "dependency", - "version" + "env", + "restoredDependencies" ], "properties": { - "dependency": { - "description": "The resolved dependency", - "allOf": [ - { - "$ref": "#/definitions/Dependency" - } - ] + "env": { + "description": "Environment variables for the triplet.", + "type": "object", + "additionalProperties": { + "type": "string" + } }, - "version": { - "description": "The resolved version of the dependency", - "type": "string", - "properties": { - "format": { - "title": "String", - "type": "string" - } + "restoredDependencies": { + "description": "Triplet map", + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/SharedTripletDependencyInfo" } } } }, - "WorkspaceConfig": { - "description": "Configuration for the workspace.", + "SharedTripletDependencyInfo": { + "description": "Dependency information for a shared triplet.", "type": "object", "required": [ - "ndk" + "restoredBinaries", + "restoredEnv", + "restoredTriplet", + "restoredVersion" ], "properties": { - "ndk": { - "description": "The NDK version range.", - "type": "string", - "properties": { - "format": { - "title": "String", - "type": "string" - } - } - }, - "qmodIncludeDirs": { - "description": "List of directories to search during qmod creation.", - "default": [], + "restoredBinaries": { + "description": "Binaries for this triplet.", "type": "array", "items": { "type": "string" } }, - "qmodIncludeFiles": { - "description": "List of files to include in the resulting qmod.", - "default": [], - "type": "array", - "items": { + "restoredEnv": { + "description": "Restored environment variables for the triplet.", + "type": "object", + "additionalProperties": { "type": "string" } }, - "qmodOutput": { - "description": "Output path for the qmod.", - "default": null, - "type": [ - "string", - "null" + "restoredTriplet": { + "description": "Restored triplet ID of the dependency.", + "allOf": [ + { + "$ref": "#/definitions/TripletId" + } ] }, + "restoredVersion": { + "description": "Version of the dependency.", + "type": "string", + "pattern": "^(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(?:-((?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\.(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\\+([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?$" + } + } + }, + "TripletId": { + "type": "string" + }, + "WorkspaceConfig": { + "description": "Configuration for the workspace.", + "type": "object", + "properties": { "scripts": { "description": "Scripts associated with the workspace.", "default": {}, diff --git a/src/extensions/mod.rs b/src/extensions/mod.rs index 879f73f..883df56 100644 --- a/src/extensions/mod.rs +++ b/src/extensions/mod.rs @@ -1,2 +1,3 @@ pub mod package_metadata; -pub mod workspace; \ No newline at end of file +pub mod serde_utils; +pub mod workspace; diff --git a/src/extensions/package_metadata.rs b/src/extensions/package_metadata.rs index f4bcaf2..382888f 100644 --- a/src/extensions/package_metadata.rs +++ b/src/extensions/package_metadata.rs @@ -1,99 +1,5 @@ -use std::path::PathBuf; +use crate::models::package::PackageConfig; -use crate::models::{dependency::SharedDependency, package::PackageMetadata}; +pub trait PackageMetadataExtensions {} -pub trait PackageMetadataExtensions { - #[deprecated = "Use get_so_name2 instead"] - fn get_so_name(&self) -> PathBuf; - fn get_so_name2(&self) -> PathBuf; - - #[deprecated = "Use get_static_name2 instead"] - fn get_static_name(&self) -> PathBuf; - fn get_static_name2(&self) -> PathBuf; - - fn get_module_id(&self) -> String { - let fixed_name = self - .get_so_name2() - .with_extension("") - .to_str() - .unwrap() - .to_string(); - fixed_name[3..fixed_name.len()].to_string() - } -} - -impl PackageMetadataExtensions for PackageMetadata { - fn get_so_name(&self) -> PathBuf { - self.get_so_name2() - } - fn get_static_name(&self) -> PathBuf { - self.get_static_name2() - } - - fn get_so_name2(&self) -> PathBuf { - self.additional_data - .override_so_name - .clone() - .unwrap_or_else(|| { - format!( - "lib{}_{}.so", - self.id, - self.version.to_string().replace('.', "_"), - ) - }) - .into() - } - - fn get_static_name2(&self) -> PathBuf { - self.additional_data - .override_static_name - .clone() - .unwrap_or_else(|| { - format!( - "lib{}_{}.a", - self.id, - self.version.to_string().replace('.', "_"), - ) - }) - .into() - } -} - -impl PackageMetadataExtensions for SharedDependency { - fn get_so_name(&self) -> PathBuf { - self.dependency - .additional_data - .override_so_name - .clone() - .unwrap_or_else(|| { - format!( - "lib{}_{}.so", - self.dependency.id, - self.version.to_string().replace('.', "_"), - ) - }) - .into() - } - fn get_static_name(&self) -> PathBuf { - self.dependency - .additional_data - .override_static_name - .clone() - .unwrap_or_else(|| { - format!( - "lib{}_{}.a", - self.dependency.id, - self.version.to_string().replace('.', "_"), - ) - }) - .into() - } - - fn get_so_name2(&self) -> PathBuf { - todo!() - } - - fn get_static_name2(&self) -> PathBuf { - todo!() - } -} +impl PackageMetadataExtensions for PackageConfig {} diff --git a/src/extensions/serde_utils.rs b/src/extensions/serde_utils.rs new file mode 100644 index 0000000..30a006d --- /dev/null +++ b/src/extensions/serde_utils.rs @@ -0,0 +1,13 @@ +use serde::{Deserialize, Deserializer}; + +/// +/// This function is used to deserialize a value from JSON, and if the value is `null`, it will return the default value of the type `T`. +/// +pub fn deserialize_null_default<'de, D, T>(deserializer: D) -> Result +where + T: Default + Deserialize<'de>, + D: Deserializer<'de>, +{ + let opt = Option::deserialize(deserializer)?; + Ok(opt.unwrap_or_default()) +} diff --git a/src/extensions/workspace.rs b/src/extensions/workspace.rs index 75963d9..beebeb4 100644 --- a/src/extensions/workspace.rs +++ b/src/extensions/workspace.rs @@ -1,4 +1,4 @@ -use std::collections::{btree_map::Entry, BTreeMap}; +use std::collections::{BTreeMap, btree_map::Entry}; use crate::models::workspace::{WorkspaceConfig, WorkspaceScript}; diff --git a/src/gen-schema.rs b/src/gen-schema.rs index 9c8fa7f..5f58ba0 100644 --- a/src/gen-schema.rs +++ b/src/gen-schema.rs @@ -1,13 +1,14 @@ -mod models; -use std::env; +use models::{package::PackageConfig, shared_package::SharedPackageConfig}; +pub mod extensions; +pub mod models; fn main() { - let shared_schema_json = schemars::schema_for!(models::dependency::SharedPackageConfig); + let shared_schema_json = schemars::schema_for!(SharedPackageConfig); let shared_schema = serde_json::to_string_pretty(&shared_schema_json).unwrap(); std::fs::write("qpm.shared.schema.json", shared_schema).expect("Failed to write shared schema"); - let schema_json = schemars::schema_for!(models::package::PackageConfig); + let schema_json = schemars::schema_for!(PackageConfig); let schema = serde_json::to_string_pretty(&schema_json).unwrap(); std::fs::write("qpm.schema.json", schema).expect("Failed to write schema"); } diff --git a/src/lib.rs b/src/lib.rs index 882551e..26fd133 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,2 +1,2 @@ +pub mod extensions; pub mod models; -pub mod extensions; \ No newline at end of file diff --git a/src/models/backend.rs b/src/models/backend.rs deleted file mode 100644 index 563fa97..0000000 --- a/src/models/backend.rs +++ /dev/null @@ -1,15 +0,0 @@ -use schemars::JsonSchema; -use semver::Version; -use serde::{Serialize, Deserialize}; - -#[derive(Serialize, Deserialize, JsonSchema, Clone, Debug, Hash, PartialEq, Eq)] -#[allow(non_snake_case)] -#[serde(rename_all = "camelCase")] -#[schemars(description = "The package version")] -pub struct PackageVersion { - #[schemars(description = "The unique identifier of the package.")] - pub id: String, - - #[schemars(description = "The version of the package.")] - pub version: Version, -} diff --git a/src/models/dependency.rs b/src/models/dependency.rs deleted file mode 100644 index f9fe62e..0000000 --- a/src/models/dependency.rs +++ /dev/null @@ -1,49 +0,0 @@ -use schemars::JsonSchema; -use semver::{Version, VersionReq}; -use serde::{Deserialize, Serialize}; - -use super::{extra::AdditionalPackageMetadata, package::PackageConfig}; - -use crate::models::version_req::make_version_req_schema; - - -#[derive(Serialize, Deserialize, JsonSchema, Clone, Debug, Hash, Eq, PartialEq)] -#[serde(rename_all = "camelCase")] -#[schemars(description = "A dependency of the package.")] -pub struct Dependency { - pub id: String, - #[serde(deserialize_with = "cursed_semver_parser::deserialize")] - #[schemars(description = "The version range of the dependency")] - #[schemars(schema_with = "make_version_req_schema")] - pub version_range: VersionReq, - - // Should've been PackageDependencyModifier but oh well - #[deprecated = "Use PackageConfig additional_data instead"] - #[schemars(description = "Additional metadata for the dependency. Deprecated, use packageConfig.additionalData instead.")] - pub additional_data: AdditionalPackageMetadata, -} - -#[derive(Serialize, Deserialize, JsonSchema, Clone, Debug, Hash, Eq, PartialEq)] -#[serde(rename_all = "camelCase")] -#[schemars(description = "A resolved dependency of the package.")] -pub struct SharedDependency { - #[schemars(description = "The resolved dependency")] - pub dependency: Dependency, - - #[schemars(description = "The resolved version of the dependency")] - pub version: Version, -} - -/// qpm.shared.json -#[derive(Serialize, Deserialize, JsonSchema, Clone, Debug, PartialEq, Eq)] -#[allow(non_snake_case)] -#[serde(rename_all = "camelCase")] -#[schemars(description = "Shared package configuration.")] -pub struct SharedPackageConfig { - /// The package config that is stored in qpm.json, copied - #[schemars(description = "A copy of the package configuration stored in qpm.json for convenience.")] - pub config: PackageConfig, - /// The dependencies as given by self.config.resolve() - #[schemars(description = "The resolved dependencies of the package.")] - pub restored_dependencies: Vec, -} diff --git a/src/models/extra.rs b/src/models/extra.rs index 635aabb..22fe334 100644 --- a/src/models/extra.rs +++ b/src/models/extra.rs @@ -1,80 +1,6 @@ -use std::path::PathBuf; - use schemars::JsonSchema; use serde::{Deserialize, Serialize}; -#[derive(Serialize, Deserialize, JsonSchema, Clone, Debug, Hash, Eq, PartialEq, Default)] -#[serde(rename_all = "camelCase")] -#[schemars(description = "Additional metadata for the package.")] -pub struct AdditionalPackageMetadata { - /// Whether or not the package is header only - #[serde(skip_serializing_if = "Option::is_none")] - #[schemars(description = "Whether or not the package is header only")] - pub headers_only: Option, - - /// Whether or not the package is statically linked - /// DEPRECATED - #[serde(skip_serializing_if = "Option::is_none")] - #[deprecated(since="0.2.0", note="Use static_link instead")] - #[schemars(description = "Whether the package is statically linked. Deprecated, use staticLink instead.")] - pub static_linking: Option, - - /// the link to the so file - #[serde(skip_serializing_if = "Option::is_none")] - #[schemars(description = "The link to the shared object file.")] - pub so_link: Option, - - /// the link to the so file - #[serde(skip_serializing_if = "Option::is_none")] - #[schemars(description = "The link to the static library file.")] - pub static_link: Option, - - /// the link to the debug .so file - #[serde(skip_serializing_if = "Option::is_none")] - #[schemars(description = "The link to the debug shared object file.")] - pub debug_so_link: Option, - - /// the overridden so file name - #[serde(skip_serializing_if = "Option::is_none")] - #[schemars(description = "The override name for the shared object file.")] - pub override_so_name: Option, - - /// the overridden static file name - #[serde(skip_serializing_if = "Option::is_none")] - #[schemars(description = "The override name for the static library file.")] - pub override_static_name: Option, - - /// the link to the qmod - #[serde(skip_serializing_if = "Option::is_none")] - #[schemars(description = "The link to the qmod file.")] - pub mod_link: Option, - - /// Branch name of a Github repo. Only used when a valid github url is provided - #[serde(skip_serializing_if = "Option::is_none")] - #[schemars(description = "The branch name of a GitHub repository. Only used when a valid GitHub URL is provided.")] - pub branch_name: Option, - - /// Additional Compile options to be used with this package - #[serde(skip_serializing_if = "Option::is_none")] - #[schemars(description = "Additional compile options for the package.")] - pub compile_options: Option, - - /// Sub folder to use from the downloaded repo / zip, so one repo can contain multiple packages - #[serde(skip_serializing_if = "Option::is_none")] - #[schemars(description = "Sub-folder to use from the downloaded repository or zip, so one repository can contain multiple packages.")] - pub sub_folder: Option, - - /// Whether to generate the cmake files on restore - #[serde(skip_serializing_if = "Option::is_none")] - #[schemars(description = "Whether to generate CMake files on restore.")] - pub cmake: Option, - - /// Whether to generate the a toolchain JSON file [CompileOptions] describing the project setup configuration - #[serde(skip_serializing_if = "Option::is_none")] - #[schemars(description = "Path to generate a toolchain JSON file describing the project setup configuration.")] - pub toolchain_out: Option -} - /// - compileOptions (QPM.Commands.SupportedPropertiesCommand+CompileOptionsProperty): Additional options for compilation and edits to compilation related files. - Supported in: package /// Type: QPM.Commands.SupportedPropertiesCommand+CompileOptionsProperty /// - includePaths - OPTIONAL (System.String[]): Additional include paths to add, relative to the extern directory. @@ -84,24 +10,22 @@ pub struct AdditionalPackageMetadata { /// - cFlags - OPTIONAL (System.String[]): Additional C flags to add. #[derive(Serialize, Deserialize, JsonSchema, Default, Clone, Debug, Hash, Eq, PartialEq)] #[serde(rename_all = "camelCase")] -#[schemars(description = "Additional options for compilation and edits to compilation related files.")] -pub struct CompileOptions { - /// Additional include paths to add, relative to the extern directory. +#[schemars( + description = "Additional options for compilation and edits to compilation related files." +)] +pub struct PackageTripletCompileOptions { + /// Additional include paths to add, relative to the extern package's shared directory. #[serde(skip_serializing_if = "Option::is_none")] - #[schemars(description = "Additional include paths to add, relative to the extern directory.")] + #[schemars(description = "Additional include paths to add, relative to the extern package's shared directory.")] pub include_paths: Option>, - /// Additional system include paths to add, relative to the extern directory. + /// Additional system include paths to add, relative to the extern package's shared directory. #[serde(skip_serializing_if = "Option::is_none")] - #[schemars(description = "Additional system include paths to add, relative to the extern directory.")] + #[schemars( + description = "Additional system include paths to add, relative to the extern package's shared directory." + )] pub system_includes: Option>, - /// Additional C++ features to add. - #[serde(skip_serializing_if = "Option::is_none")] - #[deprecated(since="0.2.1", note="Unused and exclusive to CMake")] - #[schemars(description = "Additional C++ features to add. Deprecated, unused and exclusive to CMake.")] - pub cpp_features: Option>, - /// Additional C++ flags to add. #[serde(skip_serializing_if = "Option::is_none")] #[schemars(description = "Additional C++ flags to add.")] @@ -113,62 +37,30 @@ pub struct CompileOptions { pub c_flags: Option>, } -#[derive(Serialize, Deserialize, JsonSchema, Clone, Debug, Hash, Eq, PartialEq)] -#[serde(rename_all = "camelCase")] -#[schemars(description = "Describes the dependency type.")] -pub enum DependencyLibType { - #[schemars(description = "Shared library")] - Shared, // shared - - #[schemars(description = "Static library")] - Static, // statically link - - #[schemars(description = "Header only")] - HeaderOnly // Only restore headers, don't link +impl PackageTripletCompileOptions { + pub fn merge(self, other: PackageTripletCompileOptions) -> Self { + Self { + c_flags: self.c_flags.or(other.c_flags), + cpp_flags: self.cpp_flags.or(other.cpp_flags), + include_paths: self.include_paths.or(other.include_paths), + system_includes: self.system_includes.or(other.system_includes), + } + } } -// Modifies how a package should be restored in qpm.json -#[derive(Serialize, Deserialize, JsonSchema, Clone, Debug, Hash, Eq, PartialEq, Default)] -#[serde(rename_all = "camelCase")] -#[schemars(description = "Modifies how a dependency should be restored.")] -pub struct PackageDependencyModifier { - /// Copy a dependency from a location that is local to this root path instead of from a remote url - /// Technically just a dependency field - #[serde(skip_serializing_if = "Option::is_none")] - #[schemars(description = "Copy a dependency from a location that is local to this root path instead of from a remote URL.")] - pub local_path: Option, - - /// By default if empty, true - /// If false, this mod dependency will NOT be included in the generated mod.json - /// Technically just a dependency field - #[serde(skip_serializing_if = "Option::is_none")] - #[schemars(description = "If the mod dependency should be included in the generated mod.json. Defaults to true.")] - pub include_qmod: Option, - - /// Specify any additional files to be downloaded - #[serde(skip_serializing_if = "Option::is_none")] - #[schemars(description = "Additional files to be downloaded.")] - pub extra_files: Option>, - - /// Whether or not the dependency is private and should be used in restore - /// Technically just a dependency field - #[serde( - skip_serializing_if = "Option::is_none", - rename(serialize = "private", deserialize = "private") - )] - #[schemars(description = "Whether or not the dependency is private and should be used in restore.")] - pub is_private: Option, - - /// Specifies how to restore this dependency - #[serde( - skip_serializing_if = "Option::is_none", - )] - #[schemars(description = "Specifies how to restore this dependency.")] - pub lib_type: Option, - - /// whether the mod is optional or required. If omitted, assume Some(True) - #[serde(skip_serializing_if = "Option::is_none")] - #[serde(rename = "required")] - #[schemars(description = "Whether the mod is optional or required. If omitted, assume true.")] - pub required: Option, -} +// #[derive(Serialize, Deserialize, Clone, Debug, Default, JsonSchema, PartialEq, Eq)] +// pub struct PackageTripletSettings { +// /// Environment variables for this triplet. +// #[serde(default)] +// pub env: TripletEnvironmentMap, + +// /// Additional Compile options to be used with this package +// #[serde(skip_serializing_if = "Option::is_none")] +// #[schemars(description = "Additional compile options for the package.")] +// pub compile_options: Option, + +// /// QMod URL for this triplet +// #[serde(skip_serializing_if = "Option::is_none")] +// #[schemars(description = "QMod URL for this triplet.")] +// pub qmod_url: Option, +// } diff --git a/src/models/mod.rs b/src/models/mod.rs index 9a405fc..850c73e 100644 --- a/src/models/mod.rs +++ b/src/models/mod.rs @@ -1,6 +1,12 @@ pub mod package; -pub mod dependency; -pub mod extra; -pub mod backend; +pub mod triplet; pub mod workspace; -mod version_req; \ No newline at end of file + +pub mod extra; +pub mod shared_package; + +pub mod qpackages; + +pub mod qpkg; + +mod version_req; diff --git a/src/models/package.rs b/src/models/package.rs index 9cc02d0..3c4eae6 100644 --- a/src/models/package.rs +++ b/src/models/package.rs @@ -1,19 +1,17 @@ +use std::fmt::Display; use std::path::PathBuf; use schemars::JsonSchema; -use semver::{Version, VersionReq}; -use serde::{Deserialize, Deserializer, Serialize}; +use semver::Version; +use serde::{Deserialize, Serialize}; -use super::{ - extra::{AdditionalPackageMetadata, PackageDependencyModifier}, - workspace::WorkspaceConfig, -}; +use crate::models::triplet::PackageTripletsConfig; -use crate::models::version_req::make_version_req_schema; +use super::workspace::WorkspaceConfig; #[inline] fn default_ver() -> Version { - Version::new(0, 4, 0) + Version::new(2, 0, 0) } /// latest version @@ -23,95 +21,93 @@ pub fn package_target_version() -> Version { Version::parse(env!("CARGO_PKG_VERSION")).unwrap() } +pub const QPM_JSON: &str = "qpm2.json"; + +#[derive(Serialize, Deserialize, Clone, Debug, JsonSchema, Default, PartialEq, Eq, Hash, PartialOrd, Ord)] +pub struct DependencyId(pub String); + // qpm.json #[derive(Serialize, Deserialize, JsonSchema, Clone, Debug, PartialEq, Eq)] #[allow(non_snake_case)] #[serde(rename_all = "camelCase")] #[schemars(description = "Configuration for a package.")] pub struct PackageConfig { - #[serde(default = "default_ver")] - #[schemars(description = "The version of the package configuration.")] + /// Package ID + pub id: DependencyId, + /// Package version pub version: Version, - - #[schemars(description = "The directory where shared files are stored.")] - pub shared_dir: PathBuf, - - #[schemars(description = "The directory where dependencies are stored.")] - pub dependencies_dir: PathBuf, - - #[schemars(description = "The package metadata.")] - pub info: PackageMetadata, - // allow workspace to be null - #[serde(default, deserialize_with = "deserialize_null_default")] - #[schemars(description = "The workspace configuration.")] + /// Directory where dependencies are restored + pub dependencies_directory: PathBuf, + /// Directories shared by the package + pub shared_directory: PathBuf, + /// Workspace configuration + #[serde(default)] pub workspace: WorkspaceConfig, + /// Additional package metadata + #[serde(default)] + pub additional_data: PackageAdditionalData, + /// Package triplet configurations + pub triplets: PackageTripletsConfig, + /// Config version, defaults to 2.0.0 + #[serde(default = "default_ver")] + pub config_version: Version, + + /// Whether to generate the cmake files on restore + #[serde(skip_serializing_if = "Option::is_none")] + #[schemars(description = "Whether to generate CMake files on restore.")] + pub cmake: Option, + + /// Whether to generate the a toolchain JSON file [CompileOptions] describing the project setup configuration + #[serde(skip_serializing_if = "Option::is_none")] + #[schemars( + description = "Path to generate a toolchain JSON file describing the project setup configuration." + )] + pub toolchain_out: Option, +} + +#[derive( + Serialize, Deserialize, Clone, Debug, Default, JsonSchema, PartialEq, Eq, PartialOrd, Ord, +)] +pub struct PackageWorkspaceScripts { + /// Scripts to run before building + #[serde(default)] + pub build: Vec, +} - #[schemars(description = "The dependencies of the package.")] - pub dependencies: Vec, +#[derive( + Serialize, Deserialize, Clone, Debug, Default, JsonSchema, PartialEq, Eq, PartialOrd, Ord, +)] +pub struct PackageAdditionalData { + /// Package description + #[serde(default)] + pub description: String, + /// Package author + #[serde(default)] + pub author: String, + /// Package license + #[serde(default)] + pub license: String, } impl Default for PackageConfig { fn default() -> Self { Self { + id: DependencyId::default(), version: default_ver(), - dependencies: Default::default(), - dependencies_dir: Default::default(), - info: PackageMetadata { - name: Default::default(), - id: Default::default(), - version: Version::new(1, 0, 0), - url: Default::default(), - additional_data: Default::default(), - }, - shared_dir: Default::default(), + dependencies_directory: "extern".into(), + shared_directory: "shared".into(), workspace: Default::default(), + additional_data: PackageAdditionalData::default(), + triplets: PackageTripletsConfig::default(), + config_version: default_ver(), + cmake: None, + toolchain_out: None, } } } -// qpm.json::info -#[derive(Serialize, Deserialize, JsonSchema, Clone, Debug, Hash, Eq, PartialEq)] -#[serde(rename_all = "camelCase")] -#[schemars(description = "Metadata information about the package.")] -pub struct PackageMetadata { - #[schemars(description = "The name of the package.")] - pub name: String, - - #[schemars(description = "The unique identifier of the package.")] - pub id: String, - - #[schemars(description = "The version of the package.")] - pub version: Version, - - #[schemars(description = "The website for the package.")] - pub url: Option, - - #[schemars(description = "Additional metadata for the package.")] - pub additional_data: AdditionalPackageMetadata, -} - -// qpm.json::dependencies[] -#[derive(Serialize, Deserialize, JsonSchema, Clone, Debug, Hash, Eq, PartialEq)] -#[serde(rename_all = "camelCase")] -#[schemars(description = "A dependency of the package.")] -pub struct PackageDependency { - #[schemars(description = "The unique identifier of the dependency")] - pub id: String, - - #[serde(deserialize_with = "cursed_semver_parser::deserialize")] - #[schemars(description = "The version range of the dependency")] - #[schemars(schema_with = "make_version_req_schema")] - pub version_range: VersionReq, - - #[schemars(description = "Additional metadata for the dependency")] - pub additional_data: PackageDependencyModifier, -} - -fn deserialize_null_default<'de, D, T>(deserializer: D) -> Result -where - T: Default + Deserialize<'de>, - D: Deserializer<'de>, -{ - let opt = Option::deserialize(deserializer)?; - Ok(opt.unwrap_or_default()) +impl Display for DependencyId { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.0) + } } diff --git a/src/models/qpackages.rs b/src/models/qpackages.rs new file mode 100644 index 0000000..201bdde --- /dev/null +++ b/src/models/qpackages.rs @@ -0,0 +1,32 @@ +use schemars::JsonSchema; +use semver::Version; +use serde::{Deserialize, Serialize}; + +use crate::models::package::DependencyId; + +use super::package::PackageConfig; + +#[derive(Serialize, Deserialize, JsonSchema, Clone, Debug, PartialEq, Eq)] +pub struct QPackagesVersion { + pub id: DependencyId, + pub version: Version, +} + +#[derive(Serialize, Deserialize, JsonSchema, Clone, Debug, PartialEq, Eq)] +#[allow(non_snake_case)] +#[serde(rename_all = "camelCase")] +#[schemars(description = "The package version")] +pub struct QPackagesPackage { + /// Package Configuration + #[schemars(description = "Package Configuration")] + #[serde(rename = "config")] + pub config: PackageConfig, + + /// Checksum of the qpkg + #[schemars(description = "Checksum of the qpkg")] + pub qpkg_checksum: Option, + + /// URL of the qpkg + #[schemars(description = "URL of the qpkg")] + pub qpkg_url: String, +} diff --git a/src/models/qpkg.rs b/src/models/qpkg.rs new file mode 100644 index 0000000..3d8a4df --- /dev/null +++ b/src/models/qpkg.rs @@ -0,0 +1,36 @@ +use schemars::JsonSchema; +use serde::{Deserialize, Serialize}; +use std::{collections::HashMap, path::PathBuf}; + +use crate::models::{package::PackageConfig, triplet::TripletId}; + +pub const QPKG_JSON: &str = "qpm2.qpkg.json"; + +/// QPKG package configuration +/// Distributes a package with all triplet binaries and their headers. +#[derive(Serialize, Deserialize, JsonSchema, Clone, Debug, PartialEq, Eq)] +#[allow(non_snake_case)] +#[serde(rename_all = "camelCase")] +#[schemars( + description = "QPKG package. Distributes a package with all triplet binaries and their headers." +)] +pub struct QPkg { + /// Package configuration + #[schemars(description = "Package configuration")] + pub config: PackageConfig, + + /// The directory where the headers are located + #[schemars(description = "The directory where the headers are located")] + pub shared_dir: PathBuf, + + /// Triplet map + #[schemars(description = "Triplet map")] + pub triplets: HashMap, +} + +#[derive(Serialize, Deserialize, JsonSchema, Clone, Debug, PartialEq, Eq)] +pub struct QPkgTripletInfo { + /// Paths to the binary files + /// relative to the qpkg root + pub files: Vec, +} diff --git a/src/models/shared_package.rs b/src/models/shared_package.rs new file mode 100644 index 0000000..20484f5 --- /dev/null +++ b/src/models/shared_package.rs @@ -0,0 +1,74 @@ +use schemars::JsonSchema; +use semver::Version; +use serde::{Deserialize, Serialize}; +use std::{collections::HashMap, path::PathBuf}; + +use crate::models::triplet::{TripletEnvironmentMap, TripletId}; + +use super::package::{DependencyId, PackageConfig}; + +pub type SharedLockedTripletMap = HashMap; + +pub const QPM_SHARED_JSON: &str = "qpm2.shared.json"; + +// qpm.shared.json +#[derive(Serialize, Deserialize, JsonSchema, Clone, Debug, PartialEq, Eq)] +#[allow(non_snake_case)] +#[serde(rename_all = "camelCase")] +#[schemars(description = "Configuration for a shared package.")] +pub struct SharedPackageConfig { + /// Package name + #[schemars(description = "Package name")] + pub config: PackageConfig, + + pub restored_triplet: TripletId, + + /// Triplet map + #[schemars(description = "Triplet map")] + pub locked_triplet: SharedLockedTripletMap, +} + +#[derive(Serialize, Deserialize, JsonSchema, Clone, Debug, PartialEq, Eq)] +#[allow(non_snake_case)] +#[serde(rename_all = "camelCase")] +#[schemars(description = "Configuration for a shared triplet.")] +pub struct SharedTriplet { + /// Triplet map + #[schemars(description = "Triplet map")] + pub restored_dependencies: HashMap, + + #[schemars(description = "Environment variables for the triplet.")] + pub env: TripletEnvironmentMap, + // /// Output binaries for this triplet + // #[schemars(description = "Output binaries for this triplet.")] + // pub out_binaries: Vec, + + // default should not appear here. All triplets should be listed + // TODO: Include checksums here? + // TODO: Include qpkg urls here? +} + +#[derive(Serialize, Deserialize, JsonSchema, Clone, Debug, PartialEq, Eq)] +#[allow(non_snake_case)] +#[serde(rename_all = "camelCase")] +#[schemars(description = "Dependency information for a shared triplet.")] +pub struct SharedTripletDependencyInfo { + /// Version of the dependency + #[schemars(description = "Version of the dependency.")] + pub restored_version: Version, + + // #[schemars(schema_with = "make_version_req_schema")] + // pub version_range: VersionReq, + /// Original triplet data + /// This is the triplet ID of the original triplet that this dependency was restored from. + #[schemars(description = "Restored triplet ID of the dependency.")] + pub restored_triplet: TripletId, + + /// Binaries restored for this triplet + #[schemars(description = "Binaries for this triplet.")] + pub restored_binaries: Vec, + + /// Restored environment variables for the triplet + #[schemars(description = "Restored environment variables for the triplet.")] + pub restored_env: TripletEnvironmentMap, +} diff --git a/src/models/triplet.rs b/src/models/triplet.rs new file mode 100644 index 0000000..169b51b --- /dev/null +++ b/src/models/triplet.rs @@ -0,0 +1,285 @@ +use std::{borrow::Cow, collections::HashMap, fmt::Display, path::PathBuf}; + +use schemars::JsonSchema; +use semver::VersionReq; +use serde::{Deserialize, Serialize}; + +use super::version_req::make_version_req_schema; + +use crate::models::{extra::PackageTripletCompileOptions, package::DependencyId}; + +#[derive( + Serialize, Deserialize, Clone, Debug, JsonSchema, PartialEq, Eq, Hash, PartialOrd, Ord, +)] +#[repr(transparent)] +pub struct TripletId(pub String); + +/// Dependency ID -> Dependency +pub type TripletDependencyMap = HashMap; + +/// ENV -> VALUE +pub type TripletEnvironmentMap = HashMap; + +/// Represents the game id for a QMOD package. +pub const QPM_ENV_GAME_ID: &str = "QMOD_GAME_ID"; +/// Represents the game version for a QMOD package. +pub const QPM_ENV_GAME_VERSION: &str = "QMOD_GAME_VERSION"; + +pub fn base_triplet_id() -> TripletId { + TripletId::default() +} + +/// Package triplet configuration +#[derive(Serialize, Deserialize, Clone, Debug, JsonSchema, Default, PartialEq, Eq)] +#[allow(non_snake_case)] +#[serde(rename_all = "camelCase")] +#[schemars(description = "Configuration for a package's triplets map")] +pub struct PackageTripletsConfig { + /// Default triplet ID for this package. + #[serde(skip_serializing_if = "Option::is_none")] + pub default: Option, + + /// Default configuration for all triplets. All triplets will inherit from this. + #[serde(skip_serializing_if = "Option::is_none")] + pub base: Option, + /// Configuration for specific triplets + #[serde(flatten)] + pub specific_triplets: HashMap, +} + +impl PackageTripletsConfig { + pub fn get_default_triplet(&'_ self) -> Option> { + let default = self.default.as_ref()?; + self.get_merged_triplet(default) + } + + /// Retrieves the settings for a specific triplet, merging with default settings. + /// + /// This function looks up settings for the specified triplet and combines them with + /// the default settings to create a complete configuration. The merging strategy is: + /// + /// - Dependencies: Both specific and default dependencies are included + /// - Environment variables: Both specific and default variables are included, with specific ones taking precedence + /// - Compile options: Merged with default options if present + /// - QMod URL: Uses the specific URL if available, otherwise falls back to the default + /// + /// # Parameters + /// * `triplet` - The triplet identifier to look up settings for + /// + /// # Returns + /// * `Some(PackageTripletSettings)` if the triplet exists in the configuration + /// * `None` if the triplet is not found in the specific_triplets map + pub fn get_merged_triplet(&'_ self, triplet: &TripletId) -> Option> { + if triplet == &base_triplet_id() { + return self.base.as_ref().map(Cow::Borrowed); + } + + let Some(base) = self.base.clone() else { + return self.specific_triplets.get(triplet).map(Cow::Borrowed); + }; + + let found = self.specific_triplets.get(triplet)?.clone(); + + let mut dependencies = found.dependencies; + dependencies.extend(base.dependencies); + + let mut dev_dependencies = found.dev_dependencies; + dev_dependencies.extend(base.dev_dependencies); + + let mut env = found.env; + env.extend(base.env); + + let compile_options = found + .compile_options + .clone() + .map(|a| a.merge(base.compile_options.unwrap_or_default())); + + let qmod_include_files = found + .qmod_include_files + .into_iter() + .chain(base.qmod_include_files) + .collect(); + + let qmod_include_dirs = found + .qmod_include_dirs + .into_iter() + .chain(base.qmod_include_dirs) + .collect(); + + let package_triplet = PackageTriplet { + dependencies, + dev_dependencies, + env, + compile_options, + qmod_include_dirs, + qmod_include_files, + + out_binaries: found.out_binaries.clone().or(base.out_binaries), + qmod_url: found.qmod_url.clone().or(base.qmod_url), + qmod_id: found.qmod_id.clone().or(base.qmod_id), + qmod_template: found.qmod_template.clone().or(base.qmod_template), + qmod_output: found.qmod_output.clone().or(base.qmod_output), + ndk: found.ndk.clone().or(base.ndk), + }; + Some(Cow::Owned(package_triplet)) + } + + /// Retrieves the settings for a specific triplet without merging with default settings. + pub fn get_triplet_standalone(&self, triplet: &TripletId) -> Option<&PackageTriplet> { + if triplet == &base_triplet_id() { + return self.base.as_ref(); + } + + self.specific_triplets.get(triplet) + } + /// Retrieves the settings for a specific triplet, allowing mutable access. + pub fn get_triplet_standalone_mut( + &mut self, + triplet: &TripletId, + ) -> Option<&mut PackageTriplet> { + if triplet == &base_triplet_id() { + return self.base.as_mut(); + } + + self.specific_triplets.get_mut(triplet) + } + + /// Iterates over all triplets, including the default one. + pub fn iter_merged_triplets( + &'_ self, + ) -> impl Iterator)> { + self.specific_triplets + .keys() + .cloned() + .chain(std::iter::once(base_triplet_id())) + .filter_map(|k| { + let package_triplet = self.get_merged_triplet(&k)?; + + Some((k.clone(), package_triplet)) + }) + } + + pub fn iter_merged_specific_triplets( + &'_ self, + ) -> impl Iterator)> { + self.specific_triplets.keys().map(|k| { + let package_triplet = self.get_merged_triplet(k).unwrap(); + (k, package_triplet) + }) + } +} + +/// Triplet +#[derive(Serialize, Deserialize, Clone, Debug, Default, JsonSchema, PartialEq, Eq)] +#[allow(non_snake_case)] +#[serde(rename_all = "camelCase")] +#[schemars(description = "Configuration for a package triplet.")] +pub struct PackageTriplet { + /// Dependencies for this triplet + #[serde(default)] + pub dependencies: TripletDependencyMap, + + /// Dependencies for this triplet + #[serde(default)] + pub dev_dependencies: TripletDependencyMap, + + /// Environment variables for this triplet. + #[serde(default)] + pub env: TripletEnvironmentMap, + + /// Additional Compile options to be used with this package + #[serde(skip_serializing_if = "Option::is_none")] + #[schemars(description = "Additional compile options for the package.")] + pub compile_options: Option, + + /// QMod URL for this triplet + #[serde(skip_serializing_if = "Option::is_none")] + #[schemars(description = "QMod URL for this triplet.")] + pub qmod_url: Option, + + /// QMod ID for this triplet + #[serde(skip_serializing_if = "Option::is_none")] + #[schemars(description = "QMod ID for this triplet.")] + pub qmod_id: Option, + + #[serde(default)] + #[schemars(description = "List of directories to search during qmod creation.")] + pub qmod_include_dirs: Vec, + + #[serde(default)] + #[schemars(description = "List of files to include in the resulting qmod.")] + pub qmod_include_files: Vec, + + #[serde(default)] + #[schemars(description = "Output path for the qmod.")] + #[serde(skip_serializing_if = "Option::is_none")] + pub qmod_output: Option, + + /// QMod template path for this triplet e.g mod.template.json + #[serde(skip_serializing_if = "Option::is_none")] + #[schemars(description = "QMod template for this triplet.")] + pub qmod_template: Option, + + /// NDK Version Range + #[serde(skip_serializing_if = "Option::is_none")] + #[schemars(description = "The NDK version range.")] + #[schemars(schema_with = "make_version_req_schema")] + pub ndk: Option, + + /// Output binaries for this triplet + #[serde(skip_serializing_if = "Option::is_none")] + #[schemars(description = "Output binaries for this triplet.")] + pub out_binaries: Option>, +} + +impl PackageTriplet { + pub fn get_dependency(&self, dep_id: &DependencyId) -> Option<&PackageTripletDependency> { + self.dependencies + .get(dep_id) + .or_else(|| self.dev_dependencies.get(dep_id)) + } + + pub fn get_dependencies_combined( + &self, + ) -> impl Iterator { + self.dependencies.iter().chain(self.dev_dependencies.iter()) + } +} + +#[derive(Serialize, Deserialize, Clone, Debug, JsonSchema, Default, PartialEq, Eq)] +#[allow(non_snake_case)] +#[serde(rename_all = "camelCase")] +#[schemars(description = "Dependency information for a package triplet.")] +pub struct PackageTripletDependency { + /// Version range requirement + #[serde(rename = "versionRange")] + #[schemars(schema_with = "make_version_req_schema")] + pub version_range: VersionReq, + + /// Target triplet. `default` if null + #[schemars(description = "Target triplet for this dependency.")] + #[serde(skip_serializing_if = "Option::is_none")] + pub triplet: Option, + + /// Whether to include this dependency in the qmod + #[serde(default)] + pub qmod_export: bool, + + /// Whether this is required/optional in the qmod + /// QMod required field for this dependency + #[serde(skip_serializing_if = "Option::is_none")] + #[schemars(description = "QMod required field for this dependency.")] + pub qmod_required: Option, +} + +impl Display for TripletId { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.0) + } +} + +impl Default for TripletId { + fn default() -> Self { + TripletId("base".to_owned()) + } +} diff --git a/src/models/version_req.rs b/src/models/version_req.rs index bcb7d04..8af69eb 100644 --- a/src/models/version_req.rs +++ b/src/models/version_req.rs @@ -1,4 +1,4 @@ -use schemars::{gen::SchemaGenerator, schema::Schema, schema_for, JsonSchema}; +use schemars::{JsonSchema, r#gen::SchemaGenerator, schema::Schema, schema_for}; pub fn make_version_req_schema(generator: &mut SchemaGenerator) -> Schema { let schema = String::json_schema(generator); diff --git a/src/models/workspace.rs b/src/models/workspace.rs index 9b16a14..ac08676 100644 --- a/src/models/workspace.rs +++ b/src/models/workspace.rs @@ -1,10 +1,8 @@ -use std::{collections::BTreeMap, path::PathBuf}; +use std::collections::BTreeMap; use schemars::JsonSchema; -use semver::VersionReq; use serde::{Deserialize, Serialize}; -use crate::models::version_req::make_version_req_schema; pub type WorkspaceScript = Vec; @@ -18,21 +16,4 @@ pub struct WorkspaceConfig { #[schemars(description = "Scripts associated with the workspace.")] pub scripts: BTreeMap, - /// NDK Version Range - #[serde(skip_serializing_if = "Option::is_none")] - #[schemars(description = "The NDK version range.")] - #[schemars(schema_with = "make_version_req_schema")] - pub ndk: Option, - - #[serde(default)] - #[schemars(description = "List of directories to search during qmod creation.")] - pub qmod_include_dirs: Vec, - - #[serde(default)] - #[schemars(description = "List of files to include in the resulting qmod.")] - pub qmod_include_files: Vec, - - #[serde(default)] - #[schemars(description = "Output path for the qmod.")] - pub qmod_output: Option, }