Giter Club home page Giter Club logo

cliquickstartlib's Introduction

CLIQuickstartLib

Title: url(forResource:withExtension:subdirectory:localization:) with SwiftPM is inconsistent (at best) and causes "core dump" (at worst)

The url(forResource:withExtension:subdirectory:localization:) resource location method is (at best) inconsistent across platforms and can (at worst) cause a "core dump" when used with a Swift Package which includes resources.

CLIQuickstartLib is an "almost" minimal Swift Package Manager template with various package resources. The package provides both an executable module and a core framework library module. Resources are included for each the library, the executable and the tests.

The "not-quite" minimal part is the addition checks for various approaches to resource access.

Factors

  • platform: Linux, macOS
  • build chain execution: Linux command line interface (CLI), macOS command line interface (CLI), macOS Xcode IDE
  • top level resources directory name: Resources or something else
  • resource asset directory level: top level or some sub directory
  • use of localization

Given a package with the code and resources structure show below:

The built product resource structure varies based on the platform and build chain used

In general, url(forResource: …, withExtension: …, …) is expected to reliably handle cross-platform differences in a reliably consistent and robust way.

However, here is the observed table of what access path names work:

(subdirectory) forResource: Linux CLI macOS CLI macOS Xcode
Resources/resource_file_tool.txt 💥 👍 👍
(Resources)resource_file_tool.txt 💥 👍 👍
resource_file_tool.txt 👍 👍 🚫
Resources/resource_file_lib.txt 💥 👍 👍
(Resources)resource_file_lib.txt 💥 👍 👍
resource_file_lib.txt 👍 👍 🚫
Resources/img/watch.jpg 👍 👍 👍
(Resources/img)watch.jpg 👍 👍 👍
img/watch.jpg 👍 👍 🚫
(img)watch.jpg 👍 👍 🚫
watch.jpg 🚫 🚫 🚫
Resources_A/resource_file_lib_a.txt 👍 👍 👍
(Resources_A)resource_file_lib_a.txt 👍 👍 👍
resource_file_lib_a.txt 🚫 🚫 🚫
Resources_A/img/electricity.jpg 👍 👍 👍
(Resources_A/img)electricity.jpg 👍 👍 👍
img/electricity.jpg 🚫 🚫 🚫
(img)electricity.jpg 🚫 🚫 🚫
electricity.jpg 🚫 🚫 🚫
"en" Resources/LocalData00.json 💥 👍 🚫
"en" (Resources)LocalData00.json 💥 👍 🚫
"en" LocalData00.json 👍 👍 🚫
"es-MX" LocalData00.json 👍 👍 🚫
"en" Resources/DataFiles/…/LocalData01.json 🚫 🚫 🚫
"en" Resources_A/…/LocalData10.json 🚫 🚫 🚫
"en" Resources_A/DataFiles/…/LocalData11.json 🚫 🚫 🚫

💥 Fatal Runtime Error (Linux CLI)

# Foundation/NSCFString.swift:119: Fatal error: Constant strings cannot be deallocated
# Illegal instruction (core dumped)

Issues

  • Top level resource assets can not accessed in a uniform way across platforms.
  • Top level resource directory name affects how assets can be successfully accessed.
  • In some cases, Swift Foundation on Linux exhibits a fatal runtime error.

Cross Platform Workaround

A uniform approach which avoids cross platform #if os(…) compiler directives:

  1. Put all assets in a resources subdirectory.
  2. Provide the forResource: argument with the full sub-path name.

Original Project SetupMiscellaneousResources

TODO

  • change main.swift file to @main attribute
  • find where the resources are placed after build on various computer platforms. On macOS, include build types: command line, Xcode with Package.swift and a generated Xcode project. *

TODO: Linux Tests

Options:

CLIQuickstartTool --param=value

Original Project Setup

Summary of original steps used to create the CLIQuickstart example template.

mkdir CLIQuickstartLib
cd CLIQuickstartLib
swift package init --type library

# review & update .gitignore, as needed
nano .gitignore

Framework & Executable Modules

Create two modules: one framework CLIQuickstartLib and one executable CLIQuickstartTool. Each top level folder under Sources defines a module.

The executable module only contains the main.swift file. The core framework contains all of the tool’s actual functionality. The separation of the core framework provides for easier testing; and, the core framework can be used as a dependency in other executables.

// create core framework module
mkdir Sources/CLIQuickstartTool

Update Package.swift to define two targets — one for the CLIQuickstartTool executable module and one for the CLIQuickstartLib framework.

# edit Package.swift
nano Package.swift

Package.swift

import PackageDescription

let package = Package(
    name: "CLIQuickstartLib",
    // ...
    targets: [
        .target(
            name: "CLIQuickstartLib",
            dependencies: []),
        .target(
            name: "CLIQuickstartTool",
            dependencies: ["CLIQuickstartLib"]),
        // Test CLIQuickstartLib directly instead of CLIQuickstartTool main.swift
        .testTarget(
            name: "CLIQuickstartTests",
            dependencies: ["CLIQuickstartLib"]),
        // ...
    ]
)

Define Programmatic Entry Point

Create a new CLIQuickstartLib.swift core framework class.

# nano Sources/CLIQuickstartLib/CLIQuickstart.swift
touch Sources/CLIQuickstartLib/CLIQuickstartLib.swift
edit Sources/CLIQuickstartLib/CLIQuickstartLib.swift 
import Foundation

public final class CLIQuickstartRuntime {
    private let arguments: [String]

    public init(arguments: [String] = CommandLine.arguments) { 
        self.arguments = arguments
    }

    public func run() throws {
        print("Hello world")
    }
}

Update Sources/CLIQuickstart/main.swift to call the run() method which is in the core framework CLIQuickstart class.

# nano Sources/CLIQuickstart/main.swift
edit Sources/CLIQuickstart/main.swift
import CLIQuickstartLib

let tool = CLIQuickstart()

do {
    try tool.run()
} catch {
    print("Whoops! An error occurred: \(error)")
}

Xcode

Right-click Package.swift. Select Open With > Xcode.app.

Run

swift build
.build/debug/CLIQuickstart
# Hello World

Test

The CLIQuickstartLib framework can be tested directly. Or, a Process can be run to test the CLIQuickstart executable.

Command Line Tests

## runs 'All tests'
## path .build/architecture/debug/CLIQuickstart
swift test

Xcode Testing

Runs 'Selected Tests'. Execution path: .../DerivedData/CLIQuickstart-id/Build/Products/Debug/CLIQuickstart

Installation

To run the CLI tool from anywhere, move the executable command to some path which is present on the $PATH environment variable. For example, move the the compiled binary to /usr/local/bin or /opt/local/bin.

Note: On macOS, brew doctor may complain about file in /usr/local/bin which are not managed by Homebrew.

swift build --configuration release

macOS

# Linking ./.build/x86_64-apple-macosx10.10/release/CLIQuickstart
cd .build/x86_64-apple-macosx10.10/release

sudo mkdir -p /opt/local/bin
// -f force overwrite of existing file
cp -f CLIQuickstart /opt/local/bin/CLIQuickstart

Windows

Ubuntu

# Linking ./.build/x86_64-apple-macosx10.10/release/CLIQuickstart

cd .build/release
#cp -f CLIQuickstart /usr/local/bin/CLIQuickstart
cp -f CLIQuickstart /opt/local/bin/CLIQuickstart

Miscellaneous

  • The archive action does not code sign command-line executable products from Swift packages. (48717735) Xcode 11 release notes.
    • Workaround: Manually sign archived executables using the codesign tool before distributing them.

Resources

cliquickstartlib's People

Contributors

marc-medley avatar

Watchers

 avatar  avatar

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.