Giter Club home page Giter Club logo

packageconfig's Introduction

PackageConfig

A Swift Package that allows you to define configuration settings inside a Package.swift - this is so that tools can all keep their configs consolidated inside a single place.

Tool builders use this dependency to grab their config settings.

Package Configuration

The fastest way to insert a configuration in your Package.swift is to add PackageConfig to your dependencies

.package(url: "https://github.com/shibapm/PackageConfig.git", from: "0.13.0")

And add the configuration right at the bottom of your Package.swift

e.g.

#if canImport(PackageConfig)
    import PackageConfig

    let config = PackageConfiguration([
        "komondor": [
            "pre-push": "swift test",
            "pre-commit": [
                "swift test",
                "swift run swiftformat .",
                "swift run swiftlint autocorrect --path Sources/",
                "git add .",
            ],
        ],
        "rocket": [
            "after": [
            	"push",
            ],
        ],
    ]).write()
#endif

Custom Configuration Types

PackageConfig offers also the possibility to create your own configuration type

User writes:

Run this line to have empty source for PackageConfigs target generated for you.

swift run package-config

First time it should return an error error: no target named 'PackageConfigs'.

Now you can list all the required package configs anywhere in the list of targets in Package.swift like this.

// PackageConfig parses PackageConfigs target in Package.swift to extract list of dylibs to link when compiling Package.swift with configurations
.target(name: "PackageConfigs", dependencies: [
    "ExampleConfig" // some executable configuration definition dylib
])

At the very bottom of the Package.swift

#if canImport(ExampleConfig) // example config dynamic library
import ExampleConfig

// invoking write is mandatory, otherwise the config won't be written // thanks captain obvious
let exampleConfig = ExampleConfig(value: "example value").write()
#endif

If more than one dependency uses PackageConfig be sure to wrap each in

#if canImport(SomeLibraryConfig)
import SomeLibraryConfig

let someLibraryConfig = SomeLibraryConfig().write()
#endif

Be sure to invoke write method of the Config otherwise this won't work.

And then to use executable user would need to run this in the same directory as his/her project Package.swift

swift run package-config	# compiles PackageConfigs target, expecting to find a dylib in `.build` directory for each of the listed libraries configs
swift run example		# runs your library executable

Tool-dev writes:

For the sake of example lets assume your library is called Example then Package.swift would look like this:

let package = Package(
    name: "Example",
    products: [
        // notice that product with your library config should be dynamic library in order to produce dylib and allow PackageConfig to link it when building Package.swift
        .library(name: "ExampleConfig", type: .dynamic, targets: ["ExampleConfig"]),
        // 
        .executable(name: "example", targets: ["Example"]),
    ],
    dependencies: [
        .package(url: "https://github.com/shibapm/PackageConfig.git", from: "0.0.2"),
    ],
    targets: [
        .target(name: "ExampleConfig", dependencies: ["PackageConfig"]),
        .target(name: "Example", dependencies: ["ExampleConfig"]),
    ]
)

In your ExampleConfig target define ExampleConfig like this.

import PackageConfig

// it must be public for you to use in your executable target
// also you must conform to `Codable` and `PackageConfig`
public struct ExampleConfig: Codable, PackageConfig {

    // here can be whatever you want as long as your config can stay `Codable`
    let value: String

   	// here you must define your config fileName which will be used to write and read it to/from temporary directory
    public static var fileName: String { return "example-config.json" }

    // public init is also a requirement
    public init(value: String) {
	self.value = value
    }
}

Then for example in your main.swift in executable Example target you can load your config like this:

import ExampleConfig

do {
    let config = try ExampleConfig.load()
    print(config)
} catch {
    print(error)
}

Notes for library developers

Since YourConfig target is a dynamic library you must ensure that you have built it everytime when using either read or write methods of PackageConfig. When building from terminal this can be done by just running swift build.


Changelog

  • 0.0.1

    Exposes a config for Package.swift files

    #if canImport(PackageConfig)
    import PackageConfig
    
    let config = PackageConfig([
        "danger" : ["disable"],
        "linter": ["rules": ["allowSomething"]]
    ]).write()
    #endif

    This might be everything, so if in a month or two nothing really changes I'll v1 after this release.

How it all works

When you invoke YourPackage.load() it will compile the Package.swift in the current directory using swiftc.

While compiling it will try to link list of dynamic libraries listed in PackageConfigs target.

When it is compiled, PackageConfig will run and when YourPackage.write() is called your package configuration json will be written to temporary directory.

After that it will try to read the json and decode it as if it was YourPackage type, providing it back to where you have invoked load method.

Debugging

How to see the JSON from a Package.swift file

Use SPM with verbose mode:

~/d/p/o/i/s/PackageConfig  $ swift build --verbose

And grab the bit out after the first sandbox. I then changed the final arg to -fileno 1 and it printed the JSON.

/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/swiftc --driver-mode=swift -L /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift/pm/4_2 -lPackageDescription -suppress-warnings -swift-version 4.2 -I /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift/pm/4_2 -target x86_64-apple-macosx10.10 -sdk /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.14.sdk /Users/ortatherox/dev/projects/orta/ios/spm/PackageConfig/Package.swift -fileno 1

{"errors": [], "package": {"cLanguageStandard": null, "cxxLanguageStandard": null, "dependencies": [], "name": "PackageConfig", "products": [{"name": "PackageConfig", "product_type": "library", "targets": ["PackageConfig"], "type": null}], "targets": [{"dependencies": [], "exclude": [], "name": "PackageConfig", "path": null, "publicHeadersPath": null, "sources": null, "type": "regular"}, {"dependencies": [{"name": "PackageConfig", "type": "byname"}], "exclude": [], "name": "PackageConfigTests", "path": null, "publicHeadersPath": null, "sources": null, "type": "test"}]}}

How I verify this works

I run this command:

swift build; env DEBUG="*" swift run package-config-example

if you don't use fish:

swift build; DEBUG="*" swift run package-config-example

packageconfig's People

Contributors

cszatmary avatar f-meloni avatar igormuzyka avatar leogdion avatar minuscorp avatar orta avatar slaunchaman avatar stevenlambion avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar

packageconfig's Issues

Fail to run via Mint

I use to Komondor via Mint.
But Komondor fail and error messages are below.

$ mint run Komondor
๐ŸŒฑ Cloning Komondor 1.0.6
๐ŸŒฑ Resolving package
๐ŸŒฑ Building package
๐ŸŒฑ Installed Komondor 1.0.6
๐ŸŒฑ Running komondor 1.0.6...
dyld: Library not loaded: @rpath/libPackageConfig.dylib
  Referenced from: /usr/local/lib/mint/packages/github.com_shibapm_Komondor/build/1.0.6/komondor
  Reason: image not found
[1]    52719 abort      mint run Komondor

How to resolve this dylib path? Please teach me any information.

Xcode 13 / Swift 5.5 configuration failing

Greetings.

I'd be using this library through Komondor without any issue until the last Swift 5.5 release, where PackageConfig no longer is able to compile nor generate the JSON config files needed for the other tools to work. This is the whole build:

CMD: /usr/bin/xcrun --find swiftc
Using SPM version: /Applications/Xcode-13.0.0-Release.Candidate.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift/pm/libPackagePlugin.dylib
CMD: /Applications/Xcode-13.0.0-Release.Candidate.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/swiftc --driver-mode=swift -L /Applications/Xcode-13.0.0-Release.Candidate.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift/pm/libPackagePlugin.dylib -I /Applications/Xcode-13.0.0-Release.Candidate.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift/pm/libPackagePlugin.dylib -lPackageDescription -package-description-version 5.5 -L .build/debug -I .build/debug -lPackageConfig -suppress-warnings Package.swift
Package.swift:4:8: error: no such module 'PackageDescription'
import PackageDescription
       ^
Finished launching swiftc
Error(reason: "Could not find a file at /var/folders/x6/dpg51cd946x8w__7znsbz5x00000gq/T/example.config.json - something went wrong with compilation step probably")

Consider what it would look like to allow tools to provide types to the config

Somehow the runtime/compilation aspect will need to expose the set of library targets to the eval phase

#if canImport(PackageConfig)
import PackageConfig
import Danger

// Generic
let config = PackageConfig([
    "linter": ["rules": ["allowSomething"]]
])

// Extension provided by Danger
config.danger = [
   .disabled(true)
]
#endif

The settings will need to be coerced though the JSON and back somehow, but maybe codable can do that?

Warning for encodedOffset

While running Komondor, I get this warning message in the logs :

... PackageConfig/Sources/PackageConfig/DynamicLibraries.swift:46:37: warning: 'encodedOffset' is deprecated: encodedOffset has been deprecated as most common usage is incorrect. Use utf16Offset(in:) to achieve the same behavior.
return String($0.prefix(comment.encodedOffset))

Komondor v 1.0.4
PackageConfig v 0.13.0
Apple Swift version 5.3.1 (swiftlang-1200.0.39 clang-1200.0.32.6)
Target: x86_64-apple-darwin19.6.0

Compiling Package.swift fails with PackageConfig command

At this moment, PackageConfig uses the following command to compile the Package.swift file:

/Applications/Xcode-11.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/swiftc --driver-mode=swift -L /Applications/Xcode-11.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift/pm/4_2 -I /Applications/Xcode-11.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift/pm/4_2 -lPackageDescription -L .build/debug -I .build/debug -lPackageConfig -suppress-warnings Package.swift

Which throws errors:

Package.swift:6:15: error: 'init(name:platforms:pkgConfig:providers:products:dependencies:targets:swiftLanguageVersions:cLanguageStandard:cxxLanguageStandard:)' is unavailable
let package = Package(
              ^~~~~~~
PackageDescription.Package:29:12: note: 'init(name:platforms:pkgConfig:providers:products:dependencies:targets:swiftLanguageVersions:cLanguageStandard:cxxLanguageStandard:)' was introduced in PackageDescription 5
    public init(name: String, platforms: [PackageDescription.SupportedPlatform]? = nil, pkgConfig: String? = nil, providers: [PackageDescription.SystemPackageProvider]? = nil, products: [PackageDescription.Product] = [], dependencies: [PackageDescription.Package.Dependency] = [], targets: [PackageDescription.Target] = [], swiftLanguageVersions: [PackageDescription.SwiftVersion]? = nil, cLanguageStandard: PackageDescription.CLanguageStandard? = nil, cxxLanguageStandard: PackageDescription.CXXLanguageStandard? = nil)
           ^
Package.swift:45:10: error: 'target(name:dependencies:path:exclude:sources:publicHeadersPath:)' is unavailable
        .target(
         ^~~~~~
PackageDescription.Target:34:24: note: 'target(name:dependencies:path:exclude:sources:publicHeadersPath:)' was introduced in PackageDescription 4
    public static func target(name: String, dependencies: [PackageDescription.Target.Dependency] = [], path: String? = nil, exclude: [String] = [], sources: [String]? = nil, publicHeadersPath: String? = nil) -> PackageDescription.Target
                       ^
Package.swift:49:10: error: 'target(name:dependencies:path:exclude:sources:publicHeadersPath:)' is unavailable
        .target(
         ^~~~~~
PackageDescription.Target:34:24: note: 'target(name:dependencies:path:exclude:sources:publicHeadersPath:)' was introduced in PackageDescription 4
    public static func target(name: String, dependencies: [PackageDescription.Target.Dependency] = [], path: String? = nil, exclude: [String] = [], sources: [String]? = nil, publicHeadersPath: String? = nil) -> PackageDescription.Target
                       ^
Package.swift:53:10: error: 'target(name:dependencies:path:exclude:sources:publicHeadersPath:)' is unavailable
        .target(
         ^~~~~~
PackageDescription.Target:34:24: note: 'target(name:dependencies:path:exclude:sources:publicHeadersPath:)' was introduced in PackageDescription 4
    public static func target(name: String, dependencies: [PackageDescription.Target.Dependency] = [], path: String? = nil, exclude: [String] = [], sources: [String]? = nil, publicHeadersPath: String? = nil) -> PackageDescription.Target
                       ^
Package.swift:57:10: error: 'testTarget(name:dependencies:path:exclude:sources:)' is unavailable
        .testTarget(name: "MiniSwiftTests",dependencies: ["Mini", "TestMiddleware", "NIOConcurrencyHelpers", "RxSwift", "Nimble", "RxTest", "RxBlocking"]) // dev
         ^~~~~~~~~~
PackageDescription.Target:38:24: note: 'testTarget(name:dependencies:path:exclude:sources:)' was introduced in PackageDescription 4
    public static func testTarget(name: String, dependencies: [PackageDescription.Target.Dependency] = [], path: String? = nil, exclude: [String] = [], sources: [String]? = nil) -> PackageDescription.Target

My swift-tools-version is 5.0, but i can't find the directory for the PackageDescription lib anywhere in the system. The command have surely changed with Xcode 11.

PackageConfig lib linking issue

Hi,

Thank you for this great library! I've start using it recently and I came across a small problem.
The library looks by default in the .build folder for the libPackageConfig.

So, when I run the following on my package, for example:

swift build && cd .build/debug/ && ./my-executable

The result is that it cannot find the .build folder. The same happens when moving the binary in the /usr/local/bin path, which sort of make it un-usable together with Mint

Is there any plan to overcome this issue? Is it even considered as an issue?
Thank you very much

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.