Merge branch 'main' into all-contributors/add-laxmorek
This commit is contained in:
commit
e2a11e7975
|
@ -10,6 +10,88 @@
|
||||||
"name": "kalkwarf",
|
"name": "kalkwarf",
|
||||||
"avatar_url": "https://avatars1.githubusercontent.com/u/1033839?v=4",
|
"avatar_url": "https://avatars1.githubusercontent.com/u/1033839?v=4",
|
||||||
"profile": "https://github.com/kalkwarf",
|
"profile": "https://github.com/kalkwarf",
|
||||||
|
"contributions": [
|
||||||
|
"ideas",
|
||||||
|
"bug"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"login": "fortmarek",
|
||||||
|
"name": "Marek Fořt",
|
||||||
|
"avatar_url": "https://avatars0.githubusercontent.com/u/9371695?v=4",
|
||||||
|
"profile": "https://github.com/fortmarek",
|
||||||
|
"contributions": [
|
||||||
|
"ideas"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"login": "fortmarek",
|
||||||
|
"name": "Marek Fořt",
|
||||||
|
"avatar_url": "https://avatars0.githubusercontent.com/u/9371695?v=4",
|
||||||
|
"profile": "https://github.com/fortmarek",
|
||||||
|
"contributions": [
|
||||||
|
"ideas"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"login": "kwridan",
|
||||||
|
"name": "Kas",
|
||||||
|
"avatar_url": "https://avatars3.githubusercontent.com/u/11914919?v=4",
|
||||||
|
"profile": "http://www.matrixprojects.net",
|
||||||
|
"contributions": [
|
||||||
|
"code"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"login": "natanrolnik",
|
||||||
|
"name": "Natan Rolnik",
|
||||||
|
"avatar_url": "https://avatars3.githubusercontent.com/u/1164565?v=4",
|
||||||
|
"profile": "http://natanrolnik.me",
|
||||||
|
"contributions": [
|
||||||
|
"bug"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"login": "svastven",
|
||||||
|
"name": "svastven",
|
||||||
|
"avatar_url": "https://avatars0.githubusercontent.com/u/42235915?v=4",
|
||||||
|
"profile": "https://github.com/svastven",
|
||||||
|
"contributions": [
|
||||||
|
"ideas"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"login": "bhuemer",
|
||||||
|
"name": "Bernhard Huemer",
|
||||||
|
"avatar_url": "https://avatars2.githubusercontent.com/u/1212480?v=4",
|
||||||
|
"profile": "http://bhuemer.github.io",
|
||||||
|
"contributions": [
|
||||||
|
"ideas"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"login": "mollyIV",
|
||||||
|
"name": "Daniel Jankowski",
|
||||||
|
"avatar_url": "https://avatars0.githubusercontent.com/u/10795657?v=4",
|
||||||
|
"profile": "https://djankowski.dev",
|
||||||
|
"contributions": [
|
||||||
|
"ideas"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"login": "facumenzella",
|
||||||
|
"name": "Facundo Menzella",
|
||||||
|
"avatar_url": "https://avatars1.githubusercontent.com/u/1125252?v=4",
|
||||||
|
"profile": "https://github.com/facumenzella",
|
||||||
|
"contributions": [
|
||||||
|
"ideas"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"login": "eito",
|
||||||
|
"name": "Eric Ito",
|
||||||
|
"avatar_url": "https://avatars3.githubusercontent.com/u/775643?v=4",
|
||||||
|
"profile": "https://github.com/eito",
|
||||||
"contributions": [
|
"contributions": [
|
||||||
"ideas"
|
"ideas"
|
||||||
]
|
]
|
||||||
|
@ -23,6 +105,33 @@
|
||||||
"code",
|
"code",
|
||||||
"ideas"
|
"ideas"
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"login": "olejnjak",
|
||||||
|
"name": "Jakub Olejník",
|
||||||
|
"avatar_url": "https://avatars1.githubusercontent.com/u/3148214?v=4",
|
||||||
|
"profile": "https://github.com/olejnjak",
|
||||||
|
"contributions": [
|
||||||
|
"ideas"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"login": "lakpa",
|
||||||
|
"name": "ldindu",
|
||||||
|
"avatar_url": "https://avatars0.githubusercontent.com/u/389328?v=4",
|
||||||
|
"profile": "https://github.com/lakpa",
|
||||||
|
"contributions": [
|
||||||
|
"ideas"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"login": "gtsifrikas",
|
||||||
|
"name": "George Tsifrikas",
|
||||||
|
"avatar_url": "https://avatars2.githubusercontent.com/u/8904378?v=4",
|
||||||
|
"profile": "https://github.com/gtsifrikas",
|
||||||
|
"contributions": [
|
||||||
|
"blog"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"contributorsPerLine": 7,
|
"contributorsPerLine": 7,
|
||||||
|
|
|
@ -4,7 +4,7 @@ name: Checks
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
- master
|
- main
|
||||||
paths:
|
paths:
|
||||||
- Sources/**/*
|
- Sources/**/*
|
||||||
- Tests/**/*
|
- Tests/**/*
|
||||||
|
@ -21,15 +21,18 @@ jobs:
|
||||||
swiftformat:
|
swiftformat:
|
||||||
name: SwiftFormat
|
name: SwiftFormat
|
||||||
runs-on: macOS-latest
|
runs-on: macOS-latest
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
xcode: ['12.1']
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v1
|
- uses: actions/checkout@v1
|
||||||
- name: Select Xcode 11.5
|
- name: Select Xcode
|
||||||
run: sudo xcode-select -switch /Applications/Xcode_11.5.app
|
run: sudo xcode-select -switch /Applications/Xcode_${{ matrix.xcode }}.app
|
||||||
- uses: actions/setup-ruby@v1
|
- uses: actions/setup-ruby@v1
|
||||||
with:
|
with:
|
||||||
ruby-version: '2.x'
|
ruby-version: '2.x'
|
||||||
- name: Install Bundler 2.0.2
|
- name: Install Bundler 2.1.4
|
||||||
run: gem install bundler --version 2.0.2
|
run: gem install bundler --version 2.1.4
|
||||||
- name: Install Bundler dependencies
|
- name: Install Bundler dependencies
|
||||||
run: bundle install
|
run: bundle install
|
||||||
- name: Run swiftformat
|
- name: Run swiftformat
|
||||||
|
@ -37,15 +40,18 @@ jobs:
|
||||||
swiftlint:
|
swiftlint:
|
||||||
name: Swiftlint
|
name: Swiftlint
|
||||||
runs-on: macOS-latest
|
runs-on: macOS-latest
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
xcode: ['12.1']
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v1
|
- uses: actions/checkout@v1
|
||||||
- name: Select Xcode 11.5
|
- name: Select Xcode
|
||||||
run: sudo xcode-select -switch /Applications/Xcode_11.5.app
|
run: sudo xcode-select -switch /Applications/Xcode_${{ matrix.xcode }}.app
|
||||||
- uses: actions/setup-ruby@v1
|
- uses: actions/setup-ruby@v1
|
||||||
with:
|
with:
|
||||||
ruby-version: '2.x'
|
ruby-version: '2.x'
|
||||||
- name: Install Bundler 2.0.2
|
- name: Install Bundler 2.1.4
|
||||||
run: gem install bundler --version 2.0.2
|
run: gem install bundler --version 2.1.4
|
||||||
- name: Install Bundler dependencies
|
- name: Install Bundler dependencies
|
||||||
run: bundle install
|
run: bundle install
|
||||||
- name: Run swiftlint
|
- name: Run swiftlint
|
||||||
|
|
|
@ -16,10 +16,13 @@ jobs:
|
||||||
test:
|
test:
|
||||||
name: Test
|
name: Test
|
||||||
runs-on: macOS-latest
|
runs-on: macOS-latest
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
xcode: ['12.1']
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v1
|
- uses: actions/checkout@v1
|
||||||
- name: Select Xcode 11.5
|
- name: Select Xcode
|
||||||
run: sudo xcode-select -switch /Applications/Xcode_11.5.app
|
run: sudo xcode-select -switch /Applications/Xcode_${{ matrix.xcode }}.app
|
||||||
- name: Build Package
|
- name: Build Package
|
||||||
working-directory: ./tools/fixturegen
|
working-directory: ./tools/fixturegen
|
||||||
run: swift build
|
run: swift build
|
||||||
|
|
|
@ -0,0 +1,37 @@
|
||||||
|
name: Release
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
tags:
|
||||||
|
- '*'
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
release:
|
||||||
|
name: Release Tuist
|
||||||
|
runs-on: macos-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
with:
|
||||||
|
ref: main
|
||||||
|
fetch-depth: 0
|
||||||
|
- uses: actions/setup-ruby@v1
|
||||||
|
with:
|
||||||
|
ruby-version: '2.x'
|
||||||
|
# - name: Install Bundler 2.1.4
|
||||||
|
# run: gem install bundler --version 2.1.4
|
||||||
|
# - uses: actions/cache@v2
|
||||||
|
# with:
|
||||||
|
# path: vendor/bundle
|
||||||
|
# key: ${{ runner.os }}-gems-${{ hashFiles('**/Gemfile.lock') }}
|
||||||
|
# restore-keys: |
|
||||||
|
# ${{ runner.os }}-gems-
|
||||||
|
# - name: Bundle install
|
||||||
|
# run: |
|
||||||
|
# bundle config path vendor/bundle
|
||||||
|
# bundle install --jobs 4 --retry 3
|
||||||
|
# - name: Select Xcode 11.5
|
||||||
|
# run: sudo xcode-select -switch /Applications/Xcode_11.5.app
|
||||||
|
# - name: Create a new release
|
||||||
|
# uses: fortmarek/tapestry-action@0.1.1
|
||||||
|
# env:
|
||||||
|
# GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
@ -3,7 +3,7 @@ name: Tuist
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
- master
|
- main
|
||||||
paths:
|
paths:
|
||||||
- Sources/**/*
|
- Sources/**/*
|
||||||
- Tests/**/*
|
- Tests/**/*
|
||||||
|
@ -28,11 +28,18 @@ jobs:
|
||||||
runs-on: macOS-latest
|
runs-on: macOS-latest
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
xcode: ['11.5', '12_beta']
|
xcode: ['11.5', '12.1']
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v1
|
- uses: actions/checkout@v1
|
||||||
- name: Select Xcode
|
- name: Select Xcode
|
||||||
run: sudo xcode-select -switch /Applications/Xcode_${{ matrix.xcode }}.app
|
run: sudo xcode-select -switch /Applications/Xcode_${{ matrix.xcode }}.app
|
||||||
|
- uses: actions/cache@v2
|
||||||
|
name: 'Cache SPM dependencies'
|
||||||
|
with:
|
||||||
|
path: .build
|
||||||
|
key: ${{ runner.os }}-spm-${{ hashFiles('**/Package.resolved') }}
|
||||||
|
restore-keys: |
|
||||||
|
${{ runner.os }}-spm-
|
||||||
- name: Run tests
|
- name: Run tests
|
||||||
run: |
|
run: |
|
||||||
rm -rf .coverage
|
rm -rf .coverage
|
||||||
|
@ -43,11 +50,17 @@ jobs:
|
||||||
runs-on: macOS-latest
|
runs-on: macOS-latest
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
xcode: ['11.5']
|
xcode: ['11.5', '12.1']
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v1
|
- uses: actions/checkout@v1
|
||||||
- name: Select Xcode
|
- name: Select Xcode
|
||||||
run: sudo xcode-select -switch /Applications/Xcode_${{ matrix.xcode }}.app
|
run: sudo xcode-select -switch /Applications/Xcode_${{ matrix.xcode }}.app
|
||||||
|
- uses: actions/cache@v2
|
||||||
|
with:
|
||||||
|
path: .build
|
||||||
|
key: ${{ runner.os }}-spm-${{ hashFiles('**/Package.resolved') }}
|
||||||
|
restore-keys: |
|
||||||
|
${{ runner.os }}-spm-
|
||||||
- name: Build Tuist for release
|
- name: Build Tuist for release
|
||||||
run: swift build -c release --product tuist
|
run: swift build -c release --product tuist
|
||||||
- name: Build Tuistenv for release
|
- name: Build Tuistenv for release
|
||||||
|
@ -57,7 +70,7 @@ jobs:
|
||||||
runs-on: macOS-latest
|
runs-on: macOS-latest
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
xcode: ['11.5']
|
xcode: ['11.5', '12.1']
|
||||||
feature:
|
feature:
|
||||||
[
|
[
|
||||||
'generate-1',
|
'generate-1',
|
||||||
|
@ -65,34 +78,90 @@ jobs:
|
||||||
'generate-3',
|
'generate-3',
|
||||||
'generate-4',
|
'generate-4',
|
||||||
'generate-5',
|
'generate-5',
|
||||||
'generate-6',
|
|
||||||
'init',
|
'init',
|
||||||
'lint-project',
|
'lint-project',
|
||||||
'lint-code',
|
'lint-code',
|
||||||
'scaffold',
|
'scaffold',
|
||||||
'up',
|
'up',
|
||||||
'build',
|
'build',
|
||||||
|
'test',
|
||||||
]
|
]
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v1
|
- uses: actions/checkout@v1
|
||||||
- name: Select Xcode
|
- name: Select Xcode
|
||||||
run: sudo xcode-select -switch /Applications/Xcode_${{ matrix.xcode }}.app
|
run: sudo xcode-select -switch /Applications/Xcode_${{ matrix.xcode }}.app
|
||||||
|
- uses: actions/cache@v2
|
||||||
|
name: 'Cache SPM dependencies'
|
||||||
|
with:
|
||||||
|
path: .build
|
||||||
|
key: ${{ runner.os }}-${{ matrix.xcode }}-spm-${{ hashFiles('**/Package.resolved') }}
|
||||||
|
restore-keys: |
|
||||||
|
${{ runner.os }}-spm-
|
||||||
- uses: actions/setup-ruby@v1
|
- uses: actions/setup-ruby@v1
|
||||||
with:
|
with:
|
||||||
ruby-version: '2.x'
|
ruby-version: '2.x'
|
||||||
- name: Install Bundler 2.0.2
|
- name: Install Bundler 2.1.4
|
||||||
run: gem install bundler --version 2.0.2
|
run: gem install bundler --version 2.1.4
|
||||||
- name: Install Bundler dependencies
|
- uses: actions/cache@v2
|
||||||
run: bundle install
|
with:
|
||||||
|
path: vendor/bundle
|
||||||
|
key: ${{ runner.os }}-gems-${{ hashFiles('**/Gemfile.lock') }}
|
||||||
|
restore-keys: |
|
||||||
|
${{ runner.os }}-gems-
|
||||||
|
- name: Bundle install
|
||||||
|
run: |
|
||||||
|
bundle config path vendor/bundle
|
||||||
|
bundle install --jobs 4 --retry 3
|
||||||
- name: Run tests
|
- name: Run tests
|
||||||
run: FEATURE=features/${{ matrix.feature }}.feature bundle exec rake features
|
run: FEATURE=features/${{ matrix.feature }}.feature bundle exec rake features
|
||||||
cache_acceptance_tests:
|
xcode_12_acceptance_tests:
|
||||||
name: Cache Acceptance tests (${{ matrix.feature }})
|
name: Xcode 12 Acceptance tests (${{ matrix.feature }})
|
||||||
runs-on: macOS-latest
|
runs-on: macOS-latest
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
xcode: ['12_beta']
|
xcode: ['12.1']
|
||||||
feature: ['cache']
|
feature:
|
||||||
|
[
|
||||||
|
'generate-6',
|
||||||
|
'cache-xcframeworks',
|
||||||
|
'cache-frameworks',
|
||||||
|
'precompiled',
|
||||||
|
]
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v1
|
||||||
|
- name: Select Xcode
|
||||||
|
run: sudo xcode-select -switch /Applications/Xcode_${{ matrix.xcode }}.app
|
||||||
|
- uses: actions/cache@v2
|
||||||
|
name: 'Cache SPM dependencies'
|
||||||
|
with:
|
||||||
|
path: .build
|
||||||
|
key: ${{ runner.os }}-${{ matrix.xcode }}-spm-${{ hashFiles('**/Package.resolved') }}
|
||||||
|
restore-keys: |
|
||||||
|
${{ runner.os }}-spm-
|
||||||
|
- uses: actions/setup-ruby@v1
|
||||||
|
with:
|
||||||
|
ruby-version: '2.x'
|
||||||
|
- name: Install Bundler 2.1.4
|
||||||
|
run: gem install bundler --version 2.1.4
|
||||||
|
- uses: actions/cache@v2
|
||||||
|
with:
|
||||||
|
path: vendor/bundle
|
||||||
|
key: ${{ runner.os }}-gems-${{ hashFiles('**/Gemfile.lock') }}
|
||||||
|
restore-keys: |
|
||||||
|
${{ runner.os }}-gems-
|
||||||
|
- name: Bundle install
|
||||||
|
run: |
|
||||||
|
bundle config path vendor/bundle
|
||||||
|
bundle install --jobs 4 --retry 3
|
||||||
|
- name: Run tests
|
||||||
|
run: FEATURE=features/${{ matrix.feature }}.feature bundle exec rake features
|
||||||
|
upload:
|
||||||
|
if: github.ref == 'refs/heads/main'
|
||||||
|
name: Upload
|
||||||
|
runs-on: macOS-latest
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
xcode: ['12.1']
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v1
|
- uses: actions/checkout@v1
|
||||||
- name: Select Xcode
|
- name: Select Xcode
|
||||||
|
@ -100,28 +169,18 @@ jobs:
|
||||||
- uses: actions/setup-ruby@v1
|
- uses: actions/setup-ruby@v1
|
||||||
with:
|
with:
|
||||||
ruby-version: '2.x'
|
ruby-version: '2.x'
|
||||||
- name: Install Bundler 2.0.2
|
- name: Install Bundler 2.1.4
|
||||||
run: gem install bundler --version 2.0.2
|
run: gem install bundler --version 2.1.4
|
||||||
- name: Install Bundler dependencies
|
- uses: actions/cache@v2
|
||||||
run: bundle install
|
|
||||||
- name: Run tests
|
|
||||||
run: FEATURE=features/${{ matrix.feature }}.feature bundle exec rake features
|
|
||||||
|
|
||||||
upload:
|
|
||||||
if: github.ref == 'refs/heads/master'
|
|
||||||
name: Upload
|
|
||||||
runs-on: macOS-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v1
|
|
||||||
- name: Select Xcode 11.4
|
|
||||||
run: sudo xcode-select -switch /Applications/Xcode_11.5.app
|
|
||||||
- uses: actions/setup-ruby@v1
|
|
||||||
with:
|
with:
|
||||||
ruby-version: '2.x'
|
path: vendor/bundle
|
||||||
- name: Install Bundler 2.0.2
|
key: ${{ runner.os }}-gems-${{ hashFiles('**/Gemfile.lock') }}
|
||||||
run: gem install bundler --version 2.0.2
|
restore-keys: |
|
||||||
- name: Install Bundler dependencies
|
${{ runner.os }}-gems-
|
||||||
run: bundle install
|
- name: Bundle install
|
||||||
|
run: |
|
||||||
|
bundle config path vendor/bundle
|
||||||
|
bundle install --jobs 4 --retry 3
|
||||||
- name: Package build and upload it to GCS
|
- name: Package build and upload it to GCS
|
||||||
run: |
|
run: |
|
||||||
bundle exec rake package_commit
|
bundle exec rake package_commit
|
||||||
|
|
|
@ -14,10 +14,13 @@ jobs:
|
||||||
test:
|
test:
|
||||||
name: Build
|
name: Build
|
||||||
runs-on: macOS-latest
|
runs-on: macOS-latest
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
xcode: ['12.1']
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v1
|
- uses: actions/checkout@v1
|
||||||
- name: Select Xcode 11.5
|
- name: Select Xcode
|
||||||
run: sudo xcode-select -switch /Applications/Xcode_11.5.app
|
run: sudo xcode-select -switch /Applications/Xcode_${{ matrix.xcode }}.app
|
||||||
- name: Build Package
|
- name: Build Package
|
||||||
working-directory: ./tools/tuistbench
|
working-directory: ./tools/tuistbench
|
||||||
run: swift build
|
run: swift build
|
||||||
|
|
|
@ -1 +0,0 @@
|
||||||
1.0.0-beta.4
|
|
81
CHANGELOG.md
81
CHANGELOG.md
|
@ -4,6 +4,87 @@ Please, check out guidelines: https://keepachangelog.com/en/1.0.0/
|
||||||
|
|
||||||
## Next
|
## Next
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- Allow specifying Development Region via new `developmentRegion` parameter in `Config`s GenerationOption. [#1062](https://github.com/tuist/tuist/pull/1867) by [@svastven](https://github.com/svastven).
|
||||||
|
- Require the `Config.swift` file to be in the Tuist directory [#693](https://github.com/tuist/tuist/issues/693) by [@mollyIV](https://github.com/mollyIV).
|
||||||
|
- Mapper for the caching logic to locate the built products directory [#1929](https://github.com/tuist/tuist/pull/1929) by [@pepibumur](https://github.com/pepibumur).
|
||||||
|
- Extended `BuildPhaseGenerator` to generate script build phases [#1932](https://github.com/tuist/tuist/pull/1932) by [@pepibumur](https://github.com/pepibumur).
|
||||||
|
- Extend the `TargetContentHasher` to account for the `Target.scripts` attribute [#1933](https://github.com/tuist/tuist/pull/1933) by [@pepibumur](https://github.com/pepibumur).
|
||||||
|
- Extend the `CacheController` to generate projects with the build phase to locate the targets' built products directory [#1933](https://github.com/tuist/tuist/pull/1933) by [@pepibumur](https://github.com/pepibumur).
|
||||||
|
- Add support for appClip [#1854](https://github.com/tuist/tuist/pull/1854) by [@lakpa](https://github.com/lakpa).
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- Fixed non-framework/library targets having a header build phase [#367](https://github.com/tuist/tuist/issues/367) by [@eito](https://github.com/eito).
|
||||||
|
- Fixed missing profile scheme arguments when specified in manifest [#1543](https://github.com/tuist/tuist/issues/1543) by [@lakpa](https://github.com/lakpa).
|
||||||
|
- Fixed cache warming exporting unrelated .frameworks [#1939](https://github.com/tuist/tuist/pull/1939) by [@pepibumur](https://github.com/pepibumur).
|
||||||
|
- Fixed cache warming building from a clean state for every target [#1939](https://github.com/tuist/tuist/pull/1939) by [@pepibumur](https://github.com/pepibumur).
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- Some renames in the generation logic to make the generation logic easier to reason about [#1942](https://github.com/tuist/tuist/pull/1942) by [@pepibumur](https://github.com/pepibumur).
|
||||||
|
- Update some Swift dependencies [#1971](https://github.com/tuist/tuist/pull/1971) by [@pepibumur](https://github.com/pepibumur).
|
||||||
|
|
||||||
|
## 1.22.0 - Heimat
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- Autogenerated `xxx-Project` scheme is now shared [#1902](https://github.com/tuist/tuist/pull/1902) by [@fortmarek](https://github.com/fortmarek)
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- Allow build phase scripts to disable dependency analysis [#1883](https://github.com/tuist/tuist/pull/1883) by [@bhuemer](https://github.com/bhuemer).
|
||||||
|
- The default generated project does not include a LaunchScreen storyboard [#265](https://github.com/tuist/tuist/issues/265) by [@mollyIV](https://github.com/mollyIV).
|
||||||
|
|
||||||
|
## 1.21.0 - PBWerk
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- Allow ignoring cache when running tuist focus [#1879](https://github.com/tuist/tuist/pull/1879) by [@natanrolnik](https://github.com/natanrolnik).
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- Improve error message to have more actionable information [#921](https://github.com/tuist/tuist/issues/921) by [@mollyIV](https://github.com/mollyIV).
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- Fix calculation of Settings hash related to Cache commands [#1869](Fix calculation of Settings hash related to Cache commands) by [@natanrolnik](https://github.com/natanrolnik)
|
||||||
|
- Fixed handling of `.tuist_version` file if the file had a trailing line break [#1900](Allow trailing line break in `.tuist_version`) by [@kalkwarf](https://github.com/kalkwarf)
|
||||||
|
|
||||||
|
## 1.20.0 - Heideberg
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- Revert using root `.package.resolved` [#1830](https://github.com/tuist/tuist/pull/1830) by [@fortmarek](https://github.com/fortmarek)
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- Support for caching frameworks instead of xcframeworks [#1851](https://github.com/tuist/tuist/pull/1851)
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- Skip synthesizing resource accessors when the file/folder is empty [#1829](https://github.com/tuist/tuist/pull/1829) by [@fortmarek](https://github.com/fortmarek)
|
||||||
|
- The redirect after the cloud authentication is not being captured from the CLI [#1846](https://github.com/tuist/tuist/pull/1846) by [@pepibumur](https://github.com/pepibumur).
|
||||||
|
|
||||||
|
## 1.19.0 - Milano
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- Ensure `DEVELOPER_DIR` is used in all `swiftc` calls [#1819](https://github.com/tuist/tuist/pull/1819) by [@kwridan](https://github.com/kwridan)
|
||||||
|
- Fixed decoding bug on DefaultSettings [#1817](https://github.com/tuist/tuist/issues/1817) by [@jakeatoms](https://github.com/jakeatoms)
|
||||||
|
- Bool compiler error when generating accessor for plists [#1827](https://github.com/tuist/tuist/pull/1827) by [@fortmarek](https://github.com/fortmarek)
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- Add Workspace Mappers [#1767](https://github.com/tuist/tuist/pull/1767) by [@kwridan](https://github.com/kwridan)
|
||||||
|
- Extended `Config`'s generationOptions with `.disableShowEnvironmentVarsInScriptPhases`. It does what you'd think. [#1782](https://github.com/tuist/tuist/pull/1782) by [@kalkwarf](https://github.com/kalkwarf)
|
||||||
|
- Generate `xxx-Project` scheme to build and test all available targets by [#1765](https://github.com/tuist/tuist/pull/1765) by [@fortmarek](https://github.com/fortmarek)
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- The `tuist edit` command adds `Setup.swift` and `Config.swift` to the generated project if they exist. [#1745](https://github.com/tuist/tuist/pull/1745) by [@laxmorek](https://github.com/laxmorek)
|
||||||
|
|
||||||
## 1.18.1 - Manaslu
|
## 1.18.1 - Manaslu
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
12
Gemfile
12
Gemfile
|
@ -2,16 +2,16 @@
|
||||||
|
|
||||||
source "https://rubygems.org"
|
source "https://rubygems.org"
|
||||||
|
|
||||||
gem "cucumber", "~> 5.1"
|
gem "cucumber", "~> 5.2"
|
||||||
gem "rake", "~> 13.0"
|
gem "rake", "~> 13.0"
|
||||||
gem "byebug", "~> 11.1"
|
gem "byebug", "~> 11.1"
|
||||||
gem "minitest", "~> 5.14"
|
gem "minitest", "~> 5.14"
|
||||||
gem "simctl", "~> 1.6"
|
gem "simctl", "~> 1.6"
|
||||||
gem "rubocop", "~> 0.90.0"
|
gem "rubocop", "~> 1.0.0"
|
||||||
gem "encrypted-environment", "~> 0.2.0"
|
gem "encrypted-environment", "~> 0.2.0"
|
||||||
gem "google-cloud-storage", "~> 1.28"
|
gem "google-cloud-storage", "~> 1.29"
|
||||||
gem "colorize", "~> 0.8.1"
|
gem "colorize", "~> 0.8.1"
|
||||||
gem "cocoapods", "~> 1.9"
|
gem "cocoapods", "~> 1.10"
|
||||||
gem "xcodeproj", "~> 1.18"
|
gem "xcodeproj", "~> 1.19"
|
||||||
gem "highline", "~> 2.0"
|
gem "highline", "~> 2.0"
|
||||||
gem "zip", "~> 2.0.2"
|
gem "rubyzip", "~> 2.3.0"
|
||||||
|
|
81
Gemfile.lock
81
Gemfile.lock
|
@ -2,14 +2,14 @@ GEM
|
||||||
remote: https://rubygems.org/
|
remote: https://rubygems.org/
|
||||||
specs:
|
specs:
|
||||||
CFPropertyList (3.0.2)
|
CFPropertyList (3.0.2)
|
||||||
activesupport (4.2.11.3)
|
activesupport (5.2.4.4)
|
||||||
i18n (~> 0.7)
|
concurrent-ruby (~> 1.0, >= 1.0.2)
|
||||||
|
i18n (>= 0.7, < 2)
|
||||||
minitest (~> 5.1)
|
minitest (~> 5.1)
|
||||||
thread_safe (~> 0.3, >= 0.3.4)
|
|
||||||
tzinfo (~> 1.1)
|
tzinfo (~> 1.1)
|
||||||
addressable (2.7.0)
|
addressable (2.7.0)
|
||||||
public_suffix (>= 2.0.2, < 5.0)
|
public_suffix (>= 2.0.2, < 5.0)
|
||||||
algoliasearch (1.27.2)
|
algoliasearch (1.27.4)
|
||||||
httpclient (~> 2.8, >= 2.8.3)
|
httpclient (~> 2.8, >= 2.8.3)
|
||||||
json (>= 1.5.1)
|
json (>= 1.5.1)
|
||||||
ast (2.4.1)
|
ast (2.4.1)
|
||||||
|
@ -17,15 +17,14 @@ GEM
|
||||||
builder (3.2.4)
|
builder (3.2.4)
|
||||||
byebug (11.1.3)
|
byebug (11.1.3)
|
||||||
claide (1.0.3)
|
claide (1.0.3)
|
||||||
cocoapods (1.9.3)
|
cocoapods (1.10.0)
|
||||||
activesupport (>= 4.0.2, < 5)
|
addressable (~> 2.6)
|
||||||
claide (>= 1.0.2, < 2.0)
|
claide (>= 1.0.2, < 2.0)
|
||||||
cocoapods-core (= 1.9.3)
|
cocoapods-core (= 1.10.0)
|
||||||
cocoapods-deintegrate (>= 1.0.3, < 2.0)
|
cocoapods-deintegrate (>= 1.0.3, < 2.0)
|
||||||
cocoapods-downloader (>= 1.2.2, < 2.0)
|
cocoapods-downloader (>= 1.4.0, < 2.0)
|
||||||
cocoapods-plugins (>= 1.0.0, < 2.0)
|
cocoapods-plugins (>= 1.0.0, < 2.0)
|
||||||
cocoapods-search (>= 1.0.0, < 2.0)
|
cocoapods-search (>= 1.0.0, < 2.0)
|
||||||
cocoapods-stats (>= 1.0.0, < 2.0)
|
|
||||||
cocoapods-trunk (>= 1.4.0, < 2.0)
|
cocoapods-trunk (>= 1.4.0, < 2.0)
|
||||||
cocoapods-try (>= 1.1.0, < 2.0)
|
cocoapods-try (>= 1.1.0, < 2.0)
|
||||||
colored2 (~> 3.1)
|
colored2 (~> 3.1)
|
||||||
|
@ -35,21 +34,22 @@ GEM
|
||||||
molinillo (~> 0.6.6)
|
molinillo (~> 0.6.6)
|
||||||
nap (~> 1.0)
|
nap (~> 1.0)
|
||||||
ruby-macho (~> 1.4)
|
ruby-macho (~> 1.4)
|
||||||
xcodeproj (>= 1.14.0, < 2.0)
|
xcodeproj (>= 1.19.0, < 2.0)
|
||||||
cocoapods-core (1.9.3)
|
cocoapods-core (1.10.0)
|
||||||
activesupport (>= 4.0.2, < 6)
|
activesupport (> 5.0, < 6)
|
||||||
|
addressable (~> 2.6)
|
||||||
algoliasearch (~> 1.0)
|
algoliasearch (~> 1.0)
|
||||||
concurrent-ruby (~> 1.1)
|
concurrent-ruby (~> 1.1)
|
||||||
fuzzy_match (~> 2.0.4)
|
fuzzy_match (~> 2.0.4)
|
||||||
nap (~> 1.0)
|
nap (~> 1.0)
|
||||||
netrc (~> 0.11)
|
netrc (~> 0.11)
|
||||||
|
public_suffix
|
||||||
typhoeus (~> 1.0)
|
typhoeus (~> 1.0)
|
||||||
cocoapods-deintegrate (1.0.4)
|
cocoapods-deintegrate (1.0.4)
|
||||||
cocoapods-downloader (1.3.0)
|
cocoapods-downloader (1.4.0)
|
||||||
cocoapods-plugins (1.0.0)
|
cocoapods-plugins (1.0.0)
|
||||||
nap
|
nap
|
||||||
cocoapods-search (1.0.0)
|
cocoapods-search (1.0.0)
|
||||||
cocoapods-stats (1.1.0)
|
|
||||||
cocoapods-trunk (1.5.0)
|
cocoapods-trunk (1.5.0)
|
||||||
nap (>= 0.8, < 2.0)
|
nap (>= 0.8, < 2.0)
|
||||||
netrc (~> 0.11)
|
netrc (~> 0.11)
|
||||||
|
@ -57,14 +57,14 @@ GEM
|
||||||
colored2 (3.1.2)
|
colored2 (3.1.2)
|
||||||
colorize (0.8.1)
|
colorize (0.8.1)
|
||||||
concurrent-ruby (1.1.7)
|
concurrent-ruby (1.1.7)
|
||||||
cucumber (5.1.1)
|
cucumber (5.2.0)
|
||||||
builder (~> 3.2, >= 3.2.4)
|
builder (~> 3.2, >= 3.2.4)
|
||||||
cucumber-core (~> 8.0, >= 8.0.1)
|
cucumber-core (~> 8.0, >= 8.0.1)
|
||||||
cucumber-create-meta (~> 2.0, >= 2.0.2)
|
cucumber-create-meta (~> 2.0, >= 2.0.2)
|
||||||
cucumber-cucumber-expressions (~> 10.3, >= 10.3.0)
|
cucumber-cucumber-expressions (~> 10.3, >= 10.3.0)
|
||||||
cucumber-gherkin (~> 15.0, >= 15.0.2)
|
cucumber-gherkin (~> 15.0, >= 15.0.2)
|
||||||
cucumber-html-formatter (~> 9.0, >= 9.0.0)
|
cucumber-html-formatter (~> 9.0, >= 9.0.0)
|
||||||
cucumber-messages (~> 13.0, >= 13.0.1)
|
cucumber-messages (~> 13.1, >= 13.1.0)
|
||||||
cucumber-wire (~> 4.0, >= 4.0.1)
|
cucumber-wire (~> 4.0, >= 4.0.1)
|
||||||
diff-lcs (~> 1.4, >= 1.4.4)
|
diff-lcs (~> 1.4, >= 1.4.4)
|
||||||
multi_test (~> 0.1, >= 0.1.2)
|
multi_test (~> 0.1, >= 0.1.2)
|
||||||
|
@ -81,7 +81,7 @@ GEM
|
||||||
cucumber-messages (~> 13.0, >= 13.0.1)
|
cucumber-messages (~> 13.0, >= 13.0.1)
|
||||||
cucumber-html-formatter (9.0.0)
|
cucumber-html-formatter (9.0.0)
|
||||||
cucumber-messages (~> 13.0, >= 13.0.1)
|
cucumber-messages (~> 13.0, >= 13.0.1)
|
||||||
cucumber-messages (13.0.1)
|
cucumber-messages (13.1.0)
|
||||||
protobuf-cucumber (~> 3.10, >= 3.10.8)
|
protobuf-cucumber (~> 3.10, >= 3.10.8)
|
||||||
cucumber-tag-expressions (2.0.4)
|
cucumber-tag-expressions (2.0.4)
|
||||||
cucumber-wire (4.0.1)
|
cucumber-wire (4.0.1)
|
||||||
|
@ -105,13 +105,14 @@ GEM
|
||||||
fourflusher (2.3.1)
|
fourflusher (2.3.1)
|
||||||
fuzzy_match (2.0.4)
|
fuzzy_match (2.0.4)
|
||||||
gh_inspector (1.1.3)
|
gh_inspector (1.1.3)
|
||||||
google-api-client (0.43.0)
|
google-api-client (0.46.1)
|
||||||
addressable (~> 2.5, >= 2.5.1)
|
addressable (~> 2.5, >= 2.5.1)
|
||||||
googleauth (~> 0.9)
|
googleauth (~> 0.9)
|
||||||
httpclient (>= 2.8.1, < 3.0)
|
httpclient (>= 2.8.1, < 3.0)
|
||||||
mini_mime (~> 1.0)
|
mini_mime (~> 1.0)
|
||||||
representable (~> 3.0)
|
representable (~> 3.0)
|
||||||
retriable (>= 2.0, < 4.0)
|
retriable (>= 2.0, < 4.0)
|
||||||
|
rexml
|
||||||
signet (~> 0.12)
|
signet (~> 0.12)
|
||||||
google-cloud-core (1.5.0)
|
google-cloud-core (1.5.0)
|
||||||
google-cloud-env (~> 1.0)
|
google-cloud-env (~> 1.0)
|
||||||
|
@ -119,14 +120,14 @@ GEM
|
||||||
google-cloud-env (1.3.3)
|
google-cloud-env (1.3.3)
|
||||||
faraday (>= 0.17.3, < 2.0)
|
faraday (>= 0.17.3, < 2.0)
|
||||||
google-cloud-errors (1.0.1)
|
google-cloud-errors (1.0.1)
|
||||||
google-cloud-storage (1.28.0)
|
google-cloud-storage (1.29.1)
|
||||||
addressable (~> 2.5)
|
addressable (~> 2.5)
|
||||||
digest-crc (~> 0.4)
|
digest-crc (~> 0.4)
|
||||||
google-api-client (~> 0.33)
|
google-api-client (~> 0.33)
|
||||||
google-cloud-core (~> 1.2)
|
google-cloud-core (~> 1.2)
|
||||||
googleauth (~> 0.9)
|
googleauth (~> 0.9)
|
||||||
mini_mime (~> 1.0)
|
mini_mime (~> 1.0)
|
||||||
googleauth (0.13.1)
|
googleauth (0.14.0)
|
||||||
faraday (>= 0.17.3, < 2.0)
|
faraday (>= 0.17.3, < 2.0)
|
||||||
jwt (>= 1.4, < 3.0)
|
jwt (>= 1.4, < 3.0)
|
||||||
memoist (~> 0.16)
|
memoist (~> 0.16)
|
||||||
|
@ -135,9 +136,9 @@ GEM
|
||||||
signet (~> 0.14)
|
signet (~> 0.14)
|
||||||
highline (2.0.3)
|
highline (2.0.3)
|
||||||
httpclient (2.8.3)
|
httpclient (2.8.3)
|
||||||
i18n (0.9.5)
|
i18n (1.8.5)
|
||||||
concurrent-ruby (~> 1.0)
|
concurrent-ruby (~> 1.0)
|
||||||
json (2.3.0)
|
json (2.3.1)
|
||||||
jwt (2.2.2)
|
jwt (2.2.2)
|
||||||
memoist (0.16.2)
|
memoist (0.16.2)
|
||||||
middleware (0.1.0)
|
middleware (0.1.0)
|
||||||
|
@ -153,36 +154,37 @@ GEM
|
||||||
netrc (0.11.0)
|
netrc (0.11.0)
|
||||||
os (1.1.1)
|
os (1.1.1)
|
||||||
parallel (1.19.2)
|
parallel (1.19.2)
|
||||||
parser (2.7.1.4)
|
parser (2.7.2.0)
|
||||||
ast (~> 2.4.1)
|
ast (~> 2.4.1)
|
||||||
protobuf-cucumber (3.10.8)
|
protobuf-cucumber (3.10.8)
|
||||||
activesupport (>= 3.2)
|
activesupport (>= 3.2)
|
||||||
middleware
|
middleware
|
||||||
thor
|
thor
|
||||||
thread_safe
|
thread_safe
|
||||||
public_suffix (4.0.5)
|
public_suffix (4.0.6)
|
||||||
rainbow (3.0.0)
|
rainbow (3.0.0)
|
||||||
rake (13.0.1)
|
rake (13.0.1)
|
||||||
regexp_parser (1.7.1)
|
regexp_parser (1.8.2)
|
||||||
representable (3.0.4)
|
representable (3.0.4)
|
||||||
declarative (< 0.1.0)
|
declarative (< 0.1.0)
|
||||||
declarative-option (< 0.2.0)
|
declarative-option (< 0.2.0)
|
||||||
uber (< 0.2.0)
|
uber (< 0.2.0)
|
||||||
retriable (3.1.2)
|
retriable (3.1.2)
|
||||||
rexml (3.2.4)
|
rexml (3.2.4)
|
||||||
rubocop (0.90.0)
|
rubocop (1.0.0)
|
||||||
parallel (~> 1.10)
|
parallel (~> 1.10)
|
||||||
parser (>= 2.7.1.1)
|
parser (>= 2.7.1.5)
|
||||||
rainbow (>= 2.2.2, < 4.0)
|
rainbow (>= 2.2.2, < 4.0)
|
||||||
regexp_parser (>= 1.7)
|
regexp_parser (>= 1.8)
|
||||||
rexml
|
rexml
|
||||||
rubocop-ast (>= 0.3.0, < 1.0)
|
rubocop-ast (>= 0.6.0)
|
||||||
ruby-progressbar (~> 1.7)
|
ruby-progressbar (~> 1.7)
|
||||||
unicode-display_width (>= 1.4.0, < 2.0)
|
unicode-display_width (>= 1.4.0, < 2.0)
|
||||||
rubocop-ast (0.3.0)
|
rubocop-ast (1.0.1)
|
||||||
parser (>= 2.7.1.4)
|
parser (>= 2.7.1.5)
|
||||||
ruby-macho (1.4.0)
|
ruby-macho (1.4.0)
|
||||||
ruby-progressbar (1.10.1)
|
ruby-progressbar (1.10.1)
|
||||||
|
rubyzip (2.3.0)
|
||||||
signet (0.14.0)
|
signet (0.14.0)
|
||||||
addressable (~> 2.3)
|
addressable (~> 2.3)
|
||||||
faraday (>= 0.17.3, < 2.0)
|
faraday (>= 0.17.3, < 2.0)
|
||||||
|
@ -201,31 +203,30 @@ GEM
|
||||||
thread_safe (~> 0.1)
|
thread_safe (~> 0.1)
|
||||||
uber (0.1.0)
|
uber (0.1.0)
|
||||||
unicode-display_width (1.7.0)
|
unicode-display_width (1.7.0)
|
||||||
xcodeproj (1.18.0)
|
xcodeproj (1.19.0)
|
||||||
CFPropertyList (>= 2.3.3, < 4.0)
|
CFPropertyList (>= 2.3.3, < 4.0)
|
||||||
atomos (~> 0.1.3)
|
atomos (~> 0.1.3)
|
||||||
claide (>= 1.0.2, < 2.0)
|
claide (>= 1.0.2, < 2.0)
|
||||||
colored2 (~> 3.1)
|
colored2 (~> 3.1)
|
||||||
nanaimo (~> 0.3.0)
|
nanaimo (~> 0.3.0)
|
||||||
zip (2.0.2)
|
|
||||||
|
|
||||||
PLATFORMS
|
PLATFORMS
|
||||||
ruby
|
ruby
|
||||||
|
|
||||||
DEPENDENCIES
|
DEPENDENCIES
|
||||||
byebug (~> 11.1)
|
byebug (~> 11.1)
|
||||||
cocoapods (~> 1.9)
|
cocoapods (~> 1.10)
|
||||||
colorize (~> 0.8.1)
|
colorize (~> 0.8.1)
|
||||||
cucumber (~> 5.1)
|
cucumber (~> 5.2)
|
||||||
encrypted-environment (~> 0.2.0)
|
encrypted-environment (~> 0.2.0)
|
||||||
google-cloud-storage (~> 1.28)
|
google-cloud-storage (~> 1.29)
|
||||||
highline (~> 2.0)
|
highline (~> 2.0)
|
||||||
minitest (~> 5.14)
|
minitest (~> 5.14)
|
||||||
rake (~> 13.0)
|
rake (~> 13.0)
|
||||||
rubocop (~> 0.90.0)
|
rubocop (~> 1.0.0)
|
||||||
|
rubyzip (~> 2.3.0)
|
||||||
simctl (~> 1.6)
|
simctl (~> 1.6)
|
||||||
xcodeproj (~> 1.18)
|
xcodeproj (~> 1.19)
|
||||||
zip (~> 2.0.2)
|
|
||||||
|
|
||||||
BUNDLED WITH
|
BUNDLED WITH
|
||||||
2.1.2
|
2.1.4
|
||||||
|
|
|
@ -150,8 +150,8 @@
|
||||||
"repositoryURL": "https://github.com/apple/swift-argument-parser",
|
"repositoryURL": "https://github.com/apple/swift-argument-parser",
|
||||||
"state": {
|
"state": {
|
||||||
"branch": null,
|
"branch": null,
|
||||||
"revision": "7255fd547f70468e19abbac5f7964f1ef309ad92",
|
"revision": "92646c0cdbaca076c8d3d0207891785b3379cbff",
|
||||||
"version": "0.2.1"
|
"version": "0.3.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -168,8 +168,8 @@
|
||||||
"repositoryURL": "https://github.com/apple/swift-tools-support-core",
|
"repositoryURL": "https://github.com/apple/swift-tools-support-core",
|
||||||
"state": {
|
"state": {
|
||||||
"branch": null,
|
"branch": null,
|
||||||
"revision": "f39fc6c12266697b1585589090f0004903974685",
|
"revision": "243beea77d20db46647a3de4765c96e2c801c7c7",
|
||||||
"version": "0.1.10"
|
"version": "0.1.12"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -177,8 +177,8 @@
|
||||||
"repositoryURL": "https://github.com/httpswift/swifter.git",
|
"repositoryURL": "https://github.com/httpswift/swifter.git",
|
||||||
"state": {
|
"state": {
|
||||||
"branch": null,
|
"branch": null,
|
||||||
"revision": "8b5afb48ae64d4f729f0489ddcfe09c62b9c3687",
|
"revision": "9483a5d459b45c3ffd059f7b55f9638e268632fd",
|
||||||
"version": "1.4.7"
|
"version": "1.5.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -195,8 +195,8 @@
|
||||||
"repositoryURL": "https://github.com/thii/xcbeautify.git",
|
"repositoryURL": "https://github.com/thii/xcbeautify.git",
|
||||||
"state": {
|
"state": {
|
||||||
"branch": null,
|
"branch": null,
|
||||||
"revision": "77bf4eae0f090dbf7cbcabb68fcaf0e7ff9826db",
|
"revision": "fd7b0b6972809eead52b9016b383cf6d467e00b0",
|
||||||
"version": "0.8.0"
|
"version": "0.8.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -204,8 +204,8 @@
|
||||||
"repositoryURL": "https://github.com/tuist/XcodeProj",
|
"repositoryURL": "https://github.com/tuist/XcodeProj",
|
||||||
"state": {
|
"state": {
|
||||||
"branch": null,
|
"branch": null,
|
||||||
"revision": "81bb2bb333eafa68f8ecd8187a4bb56d51e78e97",
|
"revision": "2ae8e322ddbed99655802533441bc52f9ae76f24",
|
||||||
"version": "7.14.0"
|
"version": "7.17.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -231,8 +231,8 @@
|
||||||
"repositoryURL": "https://github.com/marmelroy/Zip.git",
|
"repositoryURL": "https://github.com/marmelroy/Zip.git",
|
||||||
"state": {
|
"state": {
|
||||||
"branch": null,
|
"branch": null,
|
||||||
"revision": "80b1c3005ee25b4c7ce46c4029ac3347e8d5e37e",
|
"revision": "bd19d974e8a38cc8d3a88c90c8a107386c3b8ccf",
|
||||||
"version": "2.0.0"
|
"version": "2.1.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
|
@ -27,19 +27,19 @@ let package = Package(
|
||||||
targets: ["TuistGenerator"]),
|
targets: ["TuistGenerator"]),
|
||||||
],
|
],
|
||||||
dependencies: [
|
dependencies: [
|
||||||
.package(url: "https://github.com/tuist/XcodeProj", .upToNextMajor(from: "7.11.0")),
|
.package(url: "https://github.com/tuist/XcodeProj", .upToNextMajor(from: "7.17.0")),
|
||||||
.package(url: "https://github.com/IBM-Swift/BlueSignals", .upToNextMajor(from: "1.0.21")),
|
.package(url: "https://github.com/IBM-Swift/BlueSignals", .upToNextMajor(from: "1.0.21")),
|
||||||
.package(url: "https://github.com/ReactiveX/RxSwift.git", .upToNextMajor(from: "5.0.1")),
|
.package(url: "https://github.com/ReactiveX/RxSwift.git", .upToNextMajor(from: "5.1.1")),
|
||||||
.package(url: "https://github.com/rnine/Checksum.git", .upToNextMajor(from: "1.0.2")),
|
.package(url: "https://github.com/rnine/Checksum.git", .upToNextMajor(from: "1.0.2")),
|
||||||
.package(url: "https://github.com/apple/swift-log.git", .upToNextMajor(from: "1.2.0")),
|
.package(url: "https://github.com/apple/swift-log.git", .upToNextMajor(from: "1.4.0")),
|
||||||
.package(url: "https://github.com/thii/xcbeautify.git", .upToNextMajor(from: "0.8.0")),
|
.package(url: "https://github.com/thii/xcbeautify.git", .upToNextMajor(from: "0.8.1")),
|
||||||
.package(url: "https://github.com/krzyzanowskim/CryptoSwift", .upToNextMajor(from: "1.3.0")),
|
.package(url: "https://github.com/krzyzanowskim/CryptoSwift", .upToNextMajor(from: "1.3.0")),
|
||||||
.package(url: "https://github.com/stencilproject/Stencil", .branch("master")),
|
.package(url: "https://github.com/stencilproject/Stencil", .branch("master")),
|
||||||
.package(url: "https://github.com/kishikawakatsumi/KeychainAccess.git", .upToNextMajor(from: "4.1.0")),
|
.package(url: "https://github.com/kishikawakatsumi/KeychainAccess.git", .upToNextMajor(from: "4.1.0")),
|
||||||
.package(url: "https://github.com/httpswift/swifter.git", .upToNextMajor(from: "1.4.7")),
|
.package(url: "https://github.com/httpswift/swifter.git", .upToNextMajor(from: "1.5.0")),
|
||||||
.package(url: "https://github.com/apple/swift-tools-support-core", .upToNextMinor(from: "0.1.1")),
|
.package(url: "https://github.com/apple/swift-tools-support-core", .upToNextMinor(from: "0.1.12")),
|
||||||
.package(url: "https://github.com/apple/swift-argument-parser", .upToNextMajor(from: "0.0.6")),
|
.package(url: "https://github.com/apple/swift-argument-parser", .upToNextMajor(from: "0.3.1")),
|
||||||
.package(url: "https://github.com/marmelroy/Zip.git", .upToNextMinor(from: "2.0.0")),
|
.package(url: "https://github.com/marmelroy/Zip.git", .upToNextMinor(from: "2.1.1")),
|
||||||
.package(url: "https://github.com/tuist/GraphViz.git", .branch("tuist")),
|
.package(url: "https://github.com/tuist/GraphViz.git", .branch("tuist")),
|
||||||
.package(url: "https://github.com/fortmarek/SwiftGen", .branch("stable")),
|
.package(url: "https://github.com/fortmarek/SwiftGen", .branch("stable")),
|
||||||
.package(url: "https://github.com/fortmarek/StencilSwiftKit.git", .branch("stable")),
|
.package(url: "https://github.com/fortmarek/StencilSwiftKit.git", .branch("stable")),
|
||||||
|
@ -75,11 +75,11 @@ let package = Package(
|
||||||
),
|
),
|
||||||
.target(
|
.target(
|
||||||
name: "TuistKit",
|
name: "TuistKit",
|
||||||
dependencies: ["XcodeProj", "SwiftToolsSupport-auto", "ArgumentParser", "TuistSupport", "TuistGenerator", "TuistCache", "TuistAutomation", "ProjectDescription", "Signals", "RxSwift", "RxBlocking", "Checksum", "TuistLoader", "TuistInsights", "TuistScaffold", "TuistSigning", "TuistCloud", "TuistDoc", "GraphViz", "TuistMigration"]
|
dependencies: ["XcodeProj", "SwiftToolsSupport-auto", "ArgumentParser", "TuistSupport", "TuistGenerator", "TuistCache", "TuistAutomation", "ProjectDescription", "Signals", "RxSwift", "RxBlocking", "Checksum", "TuistLoader", "TuistInsights", "TuistScaffold", "TuistSigning", "TuistDependencies", "TuistCloud", "TuistDoc", "GraphViz", "TuistMigration"]
|
||||||
),
|
),
|
||||||
.testTarget(
|
.testTarget(
|
||||||
name: "TuistKitTests",
|
name: "TuistKitTests",
|
||||||
dependencies: ["TuistKit", "TuistAutomation", "TuistSupportTesting", "TuistCoreTesting", "ProjectDescription", "RxBlocking", "TuistLoaderTesting", "TuistCacheTesting", "TuistGeneratorTesting", "TuistScaffoldTesting", "TuistCloudTesting", "TuistAutomationTesting", "TuistSigningTesting", "TuistMigrationTesting", "TuistDocTesting"]
|
dependencies: ["TuistKit", "TuistAutomation", "TuistSupportTesting", "TuistCoreTesting", "ProjectDescription", "RxBlocking", "TuistLoaderTesting", "TuistCacheTesting", "TuistGeneratorTesting", "TuistScaffoldTesting", "TuistCloudTesting", "TuistAutomationTesting", "TuistSigningTesting", "TuistDependenciesTesting", "TuistMigrationTesting", "TuistDocTesting"]
|
||||||
),
|
),
|
||||||
.testTarget(
|
.testTarget(
|
||||||
name: "TuistKitIntegrationTests",
|
name: "TuistKitIntegrationTests",
|
||||||
|
@ -151,7 +151,7 @@ let package = Package(
|
||||||
),
|
),
|
||||||
.target(
|
.target(
|
||||||
name: "TuistCacheTesting",
|
name: "TuistCacheTesting",
|
||||||
dependencies: ["TuistCache", "SwiftToolsSupport-auto", "TuistCore", "RxTest", "RxSwift"]
|
dependencies: ["TuistCache", "SwiftToolsSupport-auto", "TuistCore", "RxTest", "RxSwift", "TuistSupportTesting"]
|
||||||
),
|
),
|
||||||
.target(
|
.target(
|
||||||
name: "TuistCloud",
|
name: "TuistCloud",
|
||||||
|
@ -229,6 +229,22 @@ let package = Package(
|
||||||
name: "TuistSigningIntegrationTests",
|
name: "TuistSigningIntegrationTests",
|
||||||
dependencies: ["TuistSigning", "TuistSupportTesting", "TuistCoreTesting", "TuistSigningTesting"]
|
dependencies: ["TuistSigning", "TuistSupportTesting", "TuistCoreTesting", "TuistSigningTesting"]
|
||||||
),
|
),
|
||||||
|
.target(
|
||||||
|
name: "TuistDependencies",
|
||||||
|
dependencies: ["TuistCore", "TuistSupport"]
|
||||||
|
),
|
||||||
|
.target(
|
||||||
|
name: "TuistDependenciesTesting",
|
||||||
|
dependencies: ["TuistDependencies"]
|
||||||
|
),
|
||||||
|
.testTarget(
|
||||||
|
name: "TuistDependenciesTests",
|
||||||
|
dependencies: ["TuistDependencies", "TuistDependenciesTesting", "TuistCoreTesting", "TuistSupportTesting"]
|
||||||
|
),
|
||||||
|
.testTarget(
|
||||||
|
name: "TuistDependenciesIntegrationTests",
|
||||||
|
dependencies: ["TuistDependencies", "TuistDependenciesTesting", "TuistCoreTesting", "TuistSupportTesting"]
|
||||||
|
),
|
||||||
.target(
|
.target(
|
||||||
name: "TuistMigration",
|
name: "TuistMigration",
|
||||||
dependencies: ["TuistCore", "TuistSupport", "XcodeProj", "SwiftToolsSupport-auto"]
|
dependencies: ["TuistCore", "TuistSupport", "XcodeProj", "SwiftToolsSupport-auto"]
|
||||||
|
|
26
README.md
26
README.md
|
@ -1,8 +1,6 @@
|
||||||
<div align="center">
|
<div align="center">
|
||||||
<!-- ALL-CONTRIBUTORS-BADGE:START - Do not remove or modify this section -->
|
|
||||||
[![All Contributors](https://img.shields.io/badge/all_contributors-2-orange.svg?style=flat-square)](#contributors-)
|
|
||||||
<!-- ALL-CONTRIBUTORS-BADGE:END -->
|
|
||||||
<img src="assets/header.gif"/>
|
<img src="assets/header.gif"/>
|
||||||
|
<a href="#contributors-"><img src="https://img.shields.io/badge/all_contributors-2-orange.svg?style=flat-square" alt="All contributors"></a>
|
||||||
<img src="https://github.com/tuist/tuist/workflows/Tuist/badge.svg" alt="Tuist">
|
<img src="https://github.com/tuist/tuist/workflows/Tuist/badge.svg" alt="Tuist">
|
||||||
<img src="https://img.shields.io/github/v/release/tuist/tuist?include_prereleases&style=flat-square" alt="Latest Version">
|
<img src="https://img.shields.io/github/v/release/tuist/tuist?include_prereleases&style=flat-square" alt="Latest Version">
|
||||||
<a href="http://slack.tuist.io"><img src="http://slack.tuist.io/badge.svg" alt="Slack"></a>
|
<a href="http://slack.tuist.io"><img src="http://slack.tuist.io/badge.svg" alt="Slack"></a>
|
||||||
|
@ -17,6 +15,7 @@
|
||||||
<img src="https://img.shields.io/opencollective/all/tuistapp?style=flat-square" alt="Backers and sponsors">
|
<img src="https://img.shields.io/opencollective/all/tuistapp?style=flat-square" alt="Backers and sponsors">
|
||||||
<img src="https://img.shields.io/github/license/tuist/tuist?style=flat-square" alt="License">
|
<img src="https://img.shields.io/github/license/tuist/tuist?style=flat-square" alt="License">
|
||||||
<img src="https://img.shields.io/badge/Powered%20by-Tuist-blue" alt="Powered by Tuist">
|
<img src="https://img.shields.io/badge/Powered%20by-Tuist-blue" alt="Powered by Tuist">
|
||||||
|
<a href="#contributors-"><img src="https://img.shields.io/badge/all_contributors-3-orange.svg?style=flat-square" alt="Contributors"></a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
## What's Tuist 🕺
|
## What's Tuist 🕺
|
||||||
|
@ -55,6 +54,12 @@ The repository is a monorepo with multiple projects:
|
||||||
|
|
||||||
Do you want to know more about what Tuist can offer you? Or perhaps want to contribute to the project and you need a starting point? You can check out the [project documentation](https://tuist.io/docs/usage/getting-started/).
|
Do you want to know more about what Tuist can offer you? Or perhaps want to contribute to the project and you need a starting point? You can check out the [project documentation](https://tuist.io/docs/usage/getting-started/).
|
||||||
|
|
||||||
|
## Supported by MacStadium
|
||||||
|
|
||||||
|
MacStadium supports this project by providing Mac mini hardware that we can use for running performance tests.
|
||||||
|
|
||||||
|
<img width="200" src="assets/MacStadium.png"/>
|
||||||
|
|
||||||
## Contribute 👩💻
|
## Contribute 👩💻
|
||||||
|
|
||||||
If you are interested in contributed to the project, our documentation has a section with resources for contributors. We recommend starting from [this page](https://tuist.io/docs/contribution/getting-started/).
|
If you are interested in contributed to the project, our documentation has a section with resources for contributors. We recommend starting from [this page](https://tuist.io/docs/contribution/getting-started/).
|
||||||
|
@ -68,8 +73,21 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
|
||||||
<!-- markdownlint-disable -->
|
<!-- markdownlint-disable -->
|
||||||
<table>
|
<table>
|
||||||
<tr>
|
<tr>
|
||||||
<td align="center"><a href="https://github.com/kalkwarf"><img src="https://avatars1.githubusercontent.com/u/1033839?v=4" width="100px;" alt=""/><br /><sub><b>kalkwarf</b></sub></a><br /><a href="#ideas-kalkwarf" title="Ideas, Planning, & Feedback">🤔</a></td>
|
<td align="center"><a href="https://github.com/kalkwarf"><img src="https://avatars1.githubusercontent.com/u/1033839?v=4" width="100px;" alt=""/><br /><sub><b>kalkwarf</b></sub></a><br /><a href="#ideas-kalkwarf" title="Ideas, Planning, & Feedback">🤔</a> <a href="https://github.com/tuist/tuist/issues?q=author%3Akalkwarf" title="Bug reports">🐛</a></td>
|
||||||
|
<td align="center"><a href="https://github.com/fortmarek"><img src="https://avatars0.githubusercontent.com/u/9371695?v=4" width="100px;" alt=""/><br /><sub><b>Marek Fořt</b></sub></a><br /><a href="#ideas-fortmarek" title="Ideas, Planning, & Feedback">🤔</a></td>
|
||||||
<td align="center"><a href="https://github.com/laxmorek"><img src="https://avatars2.githubusercontent.com/u/4774319?v=4" width="100px;" alt=""/><br /><sub><b>Kamil Harasimowicz</b></sub></a><br /><a href="https://github.com/tuist/tuist/commits?author=laxmorek" title="Code">💻</a> <a href="#ideas-laxmorek" title="Ideas, Planning, & Feedback">🤔</a></td>
|
<td align="center"><a href="https://github.com/laxmorek"><img src="https://avatars2.githubusercontent.com/u/4774319?v=4" width="100px;" alt=""/><br /><sub><b>Kamil Harasimowicz</b></sub></a><br /><a href="https://github.com/tuist/tuist/commits?author=laxmorek" title="Code">💻</a> <a href="#ideas-laxmorek" title="Ideas, Planning, & Feedback">🤔</a></td>
|
||||||
|
<td align="center"><a href="http://www.matrixprojects.net"><img src="https://avatars3.githubusercontent.com/u/11914919?v=4" width="100px;" alt=""/><br /><sub><b>Kas</b></sub></a><br /><a href="https://github.com/tuist/tuist/commits?author=kwridan" title="Code">💻</a></td>
|
||||||
|
<td align="center"><a href="http://natanrolnik.me"><img src="https://avatars3.githubusercontent.com/u/1164565?v=4" width="100px;" alt=""/><br /><sub><b>Natan Rolnik</b></sub></a><br /><a href="https://github.com/tuist/tuist/issues?q=author%3Anatanrolnik" title="Bug reports">🐛</a></td>
|
||||||
|
<td align="center"><a href="https://github.com/svastven"><img src="https://avatars0.githubusercontent.com/u/42235915?v=4" width="100px;" alt=""/><br /><sub><b>svastven</b></sub></a><br /><a href="#ideas-svastven" title="Ideas, Planning, & Feedback">🤔</a></td>
|
||||||
|
<td align="center"><a href="http://bhuemer.github.io"><img src="https://avatars2.githubusercontent.com/u/1212480?v=4" width="100px;" alt=""/><br /><sub><b>Bernhard Huemer</b></sub></a><br /><a href="#ideas-bhuemer" title="Ideas, Planning, & Feedback">🤔</a></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td align="center"><a href="https://djankowski.dev"><img src="https://avatars0.githubusercontent.com/u/10795657?v=4" width="100px;" alt=""/><br /><sub><b>Daniel Jankowski</b></sub></a><br /><a href="#ideas-mollyIV" title="Ideas, Planning, & Feedback">🤔</a></td>
|
||||||
|
<td align="center"><a href="https://github.com/facumenzella"><img src="https://avatars1.githubusercontent.com/u/1125252?v=4" width="100px;" alt=""/><br /><sub><b>Facundo Menzella</b></sub></a><br /><a href="#ideas-facumenzella" title="Ideas, Planning, & Feedback">🤔</a></td>
|
||||||
|
<td align="center"><a href="https://github.com/eito"><img src="https://avatars3.githubusercontent.com/u/775643?v=4" width="100px;" alt=""/><br /><sub><b>Eric Ito</b></sub></a><br /><a href="#ideas-eito" title="Ideas, Planning, & Feedback">🤔</a></td>
|
||||||
|
<td align="center"><a href="https://github.com/olejnjak"><img src="https://avatars1.githubusercontent.com/u/3148214?v=4" width="100px;" alt=""/><br /><sub><b>Jakub Olejník</b></sub></a><br /><a href="#ideas-olejnjak" title="Ideas, Planning, & Feedback">🤔</a></td>
|
||||||
|
<td align="center"><a href="https://github.com/lakpa"><img src="https://avatars0.githubusercontent.com/u/389328?v=4" width="100px;" alt=""/><br /><sub><b>ldindu</b></sub></a><br /><a href="#ideas-lakpa" title="Ideas, Planning, & Feedback">🤔</a></td>
|
||||||
|
<td align="center"><a href="https://github.com/gtsifrikas"><img src="https://avatars2.githubusercontent.com/u/8904378?v=4" width="100px;" alt=""/><br /><sub><b>George Tsifrikas</b></sub></a><br /><a href="#blog-gtsifrikas" title="Blogposts">📝</a></td>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
|
|
14
RELEASE.md
14
RELEASE.md
|
@ -2,19 +2,9 @@
|
||||||
|
|
||||||
This document describes the process of releasing new versions of tuist.
|
This document describes the process of releasing new versions of tuist.
|
||||||
|
|
||||||
1. First make sure you are in master and the latest changes are pulled: `git pull origin master`
|
1. Determine the new version:
|
||||||
2. Ensure that the project is in a releasable state by running the tests: `swift test` and `bundle exec rake features`.
|
|
||||||
3. Determine the new version:
|
|
||||||
|
|
||||||
- Major if there's been a breaking change.
|
- Major if there's been a breaking change.
|
||||||
- Minor by default.
|
- Minor by default.
|
||||||
- Patch if it's a hotfix release.
|
- Patch if it's a hotfix release.
|
||||||
|
2. Run `tapestry github-release version-number` (eg `tapestry github-release 1.1.0`) *(Install [tapestry](https://github.com/ackeecz/tapestry) and [tuist](https://github.com/tuist/tuist) if you don't have them installed already)*.
|
||||||
4. Update the version in the `Constants.swift` file.
|
|
||||||
5. Update the `CHANGELOG.md` to include the version section.
|
|
||||||
6. Commit the changes and tag the commit with the version `git tag x.y.z`.
|
|
||||||
7. Select the Xcode version 11.5 before building the binaries: `sudo xcode-select -s /Applications/Xcode_11.5.app`.
|
|
||||||
8. Package and upload the release to GCS by running `bundle exec rake release`.
|
|
||||||
9. Upload the installation scripts to GCS by running `bundle exec rake release_scripts`.
|
|
||||||
10. Create a release on GitHub with the version as a title, the body from the CHANGELOG file, and attach the artifacts in the `build/` directory.
|
|
||||||
11. Run `tuist update` and verify that the new version is installed and runs.
|
|
23
Rakefile
23
Rakefile
|
@ -1,6 +1,7 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
SWIFTDOC_VERSION = "1.0.0-beta.4".freeze
|
SWIFTDOC_VERSION = "1.0.0-beta.4".freeze
|
||||||
|
SWIFTLINT_VERSION = "0.40.2".freeze
|
||||||
|
|
||||||
require 'rubygems'
|
require 'rubygems'
|
||||||
require 'cucumber'
|
require 'cucumber'
|
||||||
|
@ -21,7 +22,7 @@ end
|
||||||
|
|
||||||
desc("Updates swift-doc binary with the latest version available.")
|
desc("Updates swift-doc binary with the latest version available.")
|
||||||
task :swift_doc_update do
|
task :swift_doc_update do
|
||||||
root_dir = Dir.pwd.strip
|
root_dir = File.expand_path(__dir__)
|
||||||
Dir.mktmpdir do |temporary_dir|
|
Dir.mktmpdir do |temporary_dir|
|
||||||
Dir.chdir(temporary_dir) do
|
Dir.chdir(temporary_dir) do
|
||||||
system("curl", "-LO", "https://github.com/SwiftDocOrg/swift-doc/archive/#{SWIFTDOC_VERSION}.zip")
|
system("curl", "-LO", "https://github.com/SwiftDocOrg/swift-doc/archive/#{SWIFTDOC_VERSION}.zip")
|
||||||
|
@ -32,7 +33,20 @@ task :swift_doc_update do
|
||||||
system("cp", "swift-doc/swift-doc-#{SWIFTDOC_VERSION}/.build/release/swift-doc", "#{root_dir}/vendor/swift-doc")
|
system("cp", "swift-doc/swift-doc-#{SWIFTDOC_VERSION}/.build/release/swift-doc", "#{root_dir}/vendor/swift-doc")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
system("rm .swift-doc.version && echo \"#{SWIFTDOC_VERSION}\" >> .swift-doc.version")
|
File.write(File.join(root_dir, "vendor/.swiftdoc.version"), SWIFTDOC_VERSION)
|
||||||
|
end
|
||||||
|
|
||||||
|
desc("Updates swift-lint binary with the latest version available.")
|
||||||
|
task :swift_lint_update do
|
||||||
|
root_dir = File.expand_path(__dir__)
|
||||||
|
Dir.mktmpdir do |temporary_dir|
|
||||||
|
Dir.chdir(temporary_dir) do
|
||||||
|
system("curl", "-LO", "https://github.com/realm/SwiftLint/releases/download/#{SWIFTLINT_VERSION}/portable_swiftlint.zip")
|
||||||
|
extract_zip("portable_swiftlint.zip", "portable_swiftlint")
|
||||||
|
system("cp", "portable_swiftlint/swiftlint", "#{root_dir}/vendor/swiftlint")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
File.write(File.join(root_dir, "vendor/.swiftlint.version"), SWIFTLINT_VERSION)
|
||||||
end
|
end
|
||||||
|
|
||||||
desc("Formats the code style")
|
desc("Formats the code style")
|
||||||
|
@ -61,6 +75,11 @@ task :style_ruby_correct do
|
||||||
system("bundle", "exec", "rubocop", "-a")
|
system("bundle", "exec", "rubocop", "-a")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
desc("Builds and archive a release version of tuist and tuistenv for local testing.")
|
||||||
|
task :local_package do
|
||||||
|
package
|
||||||
|
end
|
||||||
|
|
||||||
desc("Builds, archives, and publishes tuist and tuistenv for release")
|
desc("Builds, archives, and publishes tuist and tuistenv for release")
|
||||||
task :release do
|
task :release do
|
||||||
decrypt_secrets
|
decrypt_secrets
|
||||||
|
|
|
@ -8,13 +8,17 @@ public struct Config: Codable, Equatable {
|
||||||
///
|
///
|
||||||
/// - xcodeProjectName(TemplateString): When passed, Tuist generates the project with the specific name on disk instead of using the project name.
|
/// - xcodeProjectName(TemplateString): When passed, Tuist generates the project with the specific name on disk instead of using the project name.
|
||||||
/// - organizationName(String): When passed, Tuist generates the project with the specific organization name.
|
/// - organizationName(String): When passed, Tuist generates the project with the specific organization name.
|
||||||
|
/// - developmentRegion(String): When passed, Tuist generates the project with the specific development region.
|
||||||
/// - disableAutogeneratedSchemes: When passed, Tuist generates the project only with custom specified schemes, autogenerated default schemes are skipped
|
/// - disableAutogeneratedSchemes: When passed, Tuist generates the project only with custom specified schemes, autogenerated default schemes are skipped
|
||||||
/// - disableSynthesizedResourceAccessors: When passed, Tuist does not synthesize resource accessors
|
/// - disableSynthesizedResourceAccessors: When passed, Tuist does not synthesize resource accessors
|
||||||
|
/// - disableShowEnvironmentVarsInScriptPhases: When passed, Tuist disables echoing the ENV in shell script build phases
|
||||||
public enum GenerationOptions: Encodable, Decodable, Equatable {
|
public enum GenerationOptions: Encodable, Decodable, Equatable {
|
||||||
case xcodeProjectName(TemplateString)
|
case xcodeProjectName(TemplateString)
|
||||||
case organizationName(String)
|
case organizationName(String)
|
||||||
|
case developmentRegion(String)
|
||||||
case disableAutogeneratedSchemes
|
case disableAutogeneratedSchemes
|
||||||
case disableSynthesizedResourceAccessors
|
case disableSynthesizedResourceAccessors
|
||||||
|
case disableShowEnvironmentVarsInScriptPhases
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Generation options.
|
/// Generation options.
|
||||||
|
@ -26,7 +30,7 @@ public struct Config: Codable, Equatable {
|
||||||
/// Cloud configuration.
|
/// Cloud configuration.
|
||||||
public let cloud: Cloud?
|
public let cloud: Cloud?
|
||||||
|
|
||||||
/// Initializes the tuist cofiguration.
|
/// Initializes the tuist configuration.
|
||||||
///
|
///
|
||||||
/// - Parameters:
|
/// - Parameters:
|
||||||
/// - compatibleXcodeVersions: List of Xcode versions the project is compatible with.
|
/// - compatibleXcodeVersions: List of Xcode versions the project is compatible with.
|
||||||
|
@ -45,7 +49,12 @@ public struct Config: Codable, Equatable {
|
||||||
|
|
||||||
extension Config.GenerationOptions {
|
extension Config.GenerationOptions {
|
||||||
enum CodingKeys: String, CodingKey {
|
enum CodingKeys: String, CodingKey {
|
||||||
case xcodeProjectName, organizationName, disableAutogeneratedSchemes, disableSynthesizedResourceAccessors
|
case xcodeProjectName
|
||||||
|
case organizationName
|
||||||
|
case developmentRegion
|
||||||
|
case disableAutogeneratedSchemes
|
||||||
|
case disableSynthesizedResourceAccessors
|
||||||
|
case disableShowEnvironmentVarsInScriptPhases
|
||||||
}
|
}
|
||||||
|
|
||||||
public init(from decoder: Decoder) throws {
|
public init(from decoder: Decoder) throws {
|
||||||
|
@ -63,6 +72,12 @@ extension Config.GenerationOptions {
|
||||||
self = .organizationName(organizationName)
|
self = .organizationName(organizationName)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if container.allKeys.contains(.developmentRegion), try container.decodeNil(forKey: .developmentRegion) == false {
|
||||||
|
var associatedValues = try container.nestedUnkeyedContainer(forKey: .developmentRegion)
|
||||||
|
let developmentRegion = try associatedValues.decode(String.self)
|
||||||
|
self = .developmentRegion(developmentRegion)
|
||||||
|
return
|
||||||
|
}
|
||||||
if container.allKeys.contains(.disableAutogeneratedSchemes), try container.decode(Bool.self, forKey: .disableAutogeneratedSchemes) {
|
if container.allKeys.contains(.disableAutogeneratedSchemes), try container.decode(Bool.self, forKey: .disableAutogeneratedSchemes) {
|
||||||
self = .disableAutogeneratedSchemes
|
self = .disableAutogeneratedSchemes
|
||||||
return
|
return
|
||||||
|
@ -71,6 +86,11 @@ extension Config.GenerationOptions {
|
||||||
self = .disableSynthesizedResourceAccessors
|
self = .disableSynthesizedResourceAccessors
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if container.allKeys.contains(.disableShowEnvironmentVarsInScriptPhases), try container.decode(Bool.self, forKey: .disableShowEnvironmentVarsInScriptPhases) {
|
||||||
|
self = .disableShowEnvironmentVarsInScriptPhases
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
throw DecodingError.dataCorrupted(.init(codingPath: decoder.codingPath, debugDescription: "Unknown enum case"))
|
throw DecodingError.dataCorrupted(.init(codingPath: decoder.codingPath, debugDescription: "Unknown enum case"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -84,10 +104,15 @@ extension Config.GenerationOptions {
|
||||||
case let .organizationName(name):
|
case let .organizationName(name):
|
||||||
var associatedValues = container.nestedUnkeyedContainer(forKey: .organizationName)
|
var associatedValues = container.nestedUnkeyedContainer(forKey: .organizationName)
|
||||||
try associatedValues.encode(name)
|
try associatedValues.encode(name)
|
||||||
|
case let .developmentRegion(developmentRegion):
|
||||||
|
var associatedValues = container.nestedUnkeyedContainer(forKey: .developmentRegion)
|
||||||
|
try associatedValues.encode(developmentRegion)
|
||||||
case .disableAutogeneratedSchemes:
|
case .disableAutogeneratedSchemes:
|
||||||
try container.encode(true, forKey: .disableAutogeneratedSchemes)
|
try container.encode(true, forKey: .disableAutogeneratedSchemes)
|
||||||
case .disableSynthesizedResourceAccessors:
|
case .disableSynthesizedResourceAccessors:
|
||||||
try container.encode(true, forKey: .disableSynthesizedResourceAccessors)
|
try container.encode(true, forKey: .disableSynthesizedResourceAccessors)
|
||||||
|
case .disableShowEnvironmentVarsInScriptPhases:
|
||||||
|
try container.encode(true, forKey: .disableShowEnvironmentVarsInScriptPhases)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -103,10 +128,14 @@ public func == (lhs: TuistConfig.GenerationOptions, rhs: TuistConfig.GenerationO
|
||||||
return lhs.rawString == rhs.rawString
|
return lhs.rawString == rhs.rawString
|
||||||
case let (.organizationName(lhs), .organizationName(rhs)):
|
case let (.organizationName(lhs), .organizationName(rhs)):
|
||||||
return lhs == rhs
|
return lhs == rhs
|
||||||
|
case let (.developmentRegion(lhs), .developmentRegion(rhs)):
|
||||||
|
return lhs == rhs
|
||||||
case (.disableAutogeneratedSchemes, .disableAutogeneratedSchemes):
|
case (.disableAutogeneratedSchemes, .disableAutogeneratedSchemes):
|
||||||
return true
|
return true
|
||||||
case (.disableSynthesizedResourceAccessors, .disableSynthesizedResourceAccessors):
|
case (.disableSynthesizedResourceAccessors, .disableSynthesizedResourceAccessors):
|
||||||
return true
|
return true
|
||||||
|
case (.disableShowEnvironmentVarsInScriptPhases, .disableShowEnvironmentVarsInScriptPhases):
|
||||||
|
return true
|
||||||
default:
|
default:
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,36 @@
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
/// Dependency contains the description of any kind of dependency of your Xcode project.
|
||||||
|
public struct Dependency: Codable, Equatable {
|
||||||
|
/// Name of the dependency
|
||||||
|
let name: String
|
||||||
|
|
||||||
|
/// Type of requirement for the given dependency
|
||||||
|
let requirement: Dependency.Requirement
|
||||||
|
|
||||||
|
public init(name: String, requirement: Dependency.Requirement) {
|
||||||
|
self.name = name
|
||||||
|
self.requirement = requirement
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Carthage dependency initailizer
|
||||||
|
/// - Parameter name: Name of the dependency
|
||||||
|
/// - Parameter requirement: Type of requirement for the given dependency
|
||||||
|
/// - Returns Dependency
|
||||||
|
public static func carthage(name: String, requirement: Dependency.Requirement) -> Dependency {
|
||||||
|
Dependency(name: name, requirement: requirement)
|
||||||
|
}
|
||||||
|
|
||||||
|
public static func == (lhs: Dependency, rhs: Dependency) -> Bool {
|
||||||
|
lhs.name == rhs.name && lhs.requirement == rhs.requirement
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public struct Dependencies: Codable, Equatable {
|
||||||
|
private let dependencies: [Dependency]
|
||||||
|
|
||||||
|
public init(_ dependencies: [Dependency]) {
|
||||||
|
self.dependencies = dependencies
|
||||||
|
dumpIfNeeded(self)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,30 @@
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
// The idea of Requirement comes from SPM PackageRequirement
|
||||||
|
// https://github.com/apple/swift-package-manager/blob/main/Sources/PackageDescription/PackageRequirement.swift
|
||||||
|
// This could be further extended for more cases
|
||||||
|
|
||||||
|
/// DependencyRequiement describes which dependency the project needs.
|
||||||
|
public extension Dependency {
|
||||||
|
enum Requirement: Codable, Equatable {
|
||||||
|
case exact(Version)
|
||||||
|
// case range(Range<Version>)
|
||||||
|
// case branch(String)
|
||||||
|
|
||||||
|
public init(from decoder: Decoder) throws {
|
||||||
|
let container = try decoder.container(keyedBy: CodingKeys.self)
|
||||||
|
self = .exact(try container.decode(Version.self, forKey: .exact))
|
||||||
|
}
|
||||||
|
|
||||||
|
public func encode(to encoder: Encoder) throws {
|
||||||
|
var container = encoder.container(keyedBy: CodingKeys.self)
|
||||||
|
if case let .exact(version) = self {
|
||||||
|
try container.encode(version, forKey: .exact)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum CodingKeys: String, CodingKey {
|
||||||
|
case exact
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -11,6 +11,7 @@ public enum Product: String, Codable, Equatable {
|
||||||
case unitTests = "unit_tests"
|
case unitTests = "unit_tests"
|
||||||
case uiTests = "ui_tests"
|
case uiTests = "ui_tests"
|
||||||
case bundle
|
case bundle
|
||||||
|
case appClip
|
||||||
|
|
||||||
// Not supported yet
|
// Not supported yet
|
||||||
case appExtension = "app_extension"
|
case appExtension = "app_extension"
|
||||||
|
|
|
@ -148,7 +148,7 @@ public enum DefaultSettings: Codable, Equatable {
|
||||||
var nestedContainer = try container.nestedUnkeyedContainer(forKey: .essential)
|
var nestedContainer = try container.nestedUnkeyedContainer(forKey: .essential)
|
||||||
let excludedKeys = try nestedContainer.decode(Set<String>.self)
|
let excludedKeys = try nestedContainer.decode(Set<String>.self)
|
||||||
self = .essential(excluding: excludedKeys)
|
self = .essential(excluding: excludedKeys)
|
||||||
case .none:
|
case .none?:
|
||||||
self = .none
|
self = .none
|
||||||
default:
|
default:
|
||||||
throw DecodingError.dataCorrupted(
|
throw DecodingError.dataCorrupted(
|
||||||
|
|
|
@ -78,7 +78,7 @@ public extension SettingsDictionary {
|
||||||
merging(["VERSIONING_SYSTEM": "apple-generic"])
|
merging(["VERSIONING_SYSTEM": "apple-generic"])
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sets "VERSION_INFO_PREFIX" to `version`. If `prefix` and `suffix` are not `nil`, they're used as `"VERSION_INFO_PREFIX"` and `"VERSION_INFO_SUFFIX"` respectively.
|
/// Sets "VERSION_INFO_STRING" to `version`. If `prefix` and `suffix` are not `nil`, they're used as `"VERSION_INFO_PREFIX"` and `"VERSION_INFO_SUFFIX"` respectively.
|
||||||
func versionInfo(_ version: String, prefix: String? = nil, suffix: String? = nil) -> SettingsDictionary {
|
func versionInfo(_ version: String, prefix: String? = nil, suffix: String? = nil) -> SettingsDictionary {
|
||||||
var versionSettings: SettingsDictionary = ["VERSION_INFO_STRING": SettingValue(version)]
|
var versionSettings: SettingsDictionary = ["VERSION_INFO_STRING": SettingValue(version)]
|
||||||
versionSettings["VERSION_INFO_PREFIX"] = prefix.map { SettingValue($0) }
|
versionSettings["VERSION_INFO_PREFIX"] = prefix.map { SettingValue($0) }
|
||||||
|
|
|
@ -37,6 +37,9 @@ public struct TargetAction: Codable, Equatable {
|
||||||
/// List of output filelist paths
|
/// List of output filelist paths
|
||||||
public let outputFileListPaths: [Path]
|
public let outputFileListPaths: [Path]
|
||||||
|
|
||||||
|
/// Whether to skip running this script in incremental builds, if nothing has changed
|
||||||
|
public let basedOnDependencyAnalysis: Bool?
|
||||||
|
|
||||||
public enum CodingKeys: String, CodingKey {
|
public enum CodingKeys: String, CodingKey {
|
||||||
case name
|
case name
|
||||||
case tool
|
case tool
|
||||||
|
@ -47,6 +50,7 @@ public struct TargetAction: Codable, Equatable {
|
||||||
case inputFileListPaths
|
case inputFileListPaths
|
||||||
case outputPaths
|
case outputPaths
|
||||||
case outputFileListPaths
|
case outputFileListPaths
|
||||||
|
case basedOnDependencyAnalysis
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Initializes the target action with its attributes.
|
/// Initializes the target action with its attributes.
|
||||||
|
@ -61,6 +65,7 @@ public struct TargetAction: Codable, Equatable {
|
||||||
/// - inputFileListPaths: List of input filelist paths.
|
/// - inputFileListPaths: List of input filelist paths.
|
||||||
/// - outputPaths: List of output file paths.
|
/// - outputPaths: List of output file paths.
|
||||||
/// - outputFileListPaths: List of output filelist paths.
|
/// - outputFileListPaths: List of output filelist paths.
|
||||||
|
/// - basedOnDependencyAnalysis: Whether to skip running this script in incremental builds
|
||||||
init(name: String,
|
init(name: String,
|
||||||
tool: String?,
|
tool: String?,
|
||||||
path: Path?,
|
path: Path?,
|
||||||
|
@ -69,7 +74,8 @@ public struct TargetAction: Codable, Equatable {
|
||||||
inputPaths: [Path] = [],
|
inputPaths: [Path] = [],
|
||||||
inputFileListPaths: [Path] = [],
|
inputFileListPaths: [Path] = [],
|
||||||
outputPaths: [Path] = [],
|
outputPaths: [Path] = [],
|
||||||
outputFileListPaths: [Path] = [])
|
outputFileListPaths: [Path] = [],
|
||||||
|
basedOnDependencyAnalysis: Bool? = nil)
|
||||||
{
|
{
|
||||||
self.name = name
|
self.name = name
|
||||||
self.path = path
|
self.path = path
|
||||||
|
@ -80,6 +86,7 @@ public struct TargetAction: Codable, Equatable {
|
||||||
self.inputFileListPaths = inputFileListPaths
|
self.inputFileListPaths = inputFileListPaths
|
||||||
self.outputPaths = outputPaths
|
self.outputPaths = outputPaths
|
||||||
self.outputFileListPaths = outputFileListPaths
|
self.outputFileListPaths = outputFileListPaths
|
||||||
|
self.basedOnDependencyAnalysis = basedOnDependencyAnalysis
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a target action that gets executed before the sources and resources build phase.
|
/// Returns a target action that gets executed before the sources and resources build phase.
|
||||||
|
@ -92,6 +99,7 @@ public struct TargetAction: Codable, Equatable {
|
||||||
/// - inputFileListPaths: List of input filelist paths.
|
/// - inputFileListPaths: List of input filelist paths.
|
||||||
/// - outputPaths: List of output file paths.
|
/// - outputPaths: List of output file paths.
|
||||||
/// - outputFileListPaths: List of output filelist paths.
|
/// - outputFileListPaths: List of output filelist paths.
|
||||||
|
/// - basedOnDependencyAnalysis: Whether to skip running this script in incremental builds
|
||||||
/// - Returns: Target action.
|
/// - Returns: Target action.
|
||||||
public static func pre(tool: String,
|
public static func pre(tool: String,
|
||||||
arguments: String...,
|
arguments: String...,
|
||||||
|
@ -99,7 +107,8 @@ public struct TargetAction: Codable, Equatable {
|
||||||
inputPaths: [Path] = [],
|
inputPaths: [Path] = [],
|
||||||
inputFileListPaths: [Path] = [],
|
inputFileListPaths: [Path] = [],
|
||||||
outputPaths: [Path] = [],
|
outputPaths: [Path] = [],
|
||||||
outputFileListPaths: [Path] = []) -> TargetAction
|
outputFileListPaths: [Path] = [],
|
||||||
|
basedOnDependencyAnalysis: Bool? = nil) -> TargetAction
|
||||||
{
|
{
|
||||||
TargetAction(name: name,
|
TargetAction(name: name,
|
||||||
tool: tool,
|
tool: tool,
|
||||||
|
@ -109,7 +118,8 @@ public struct TargetAction: Codable, Equatable {
|
||||||
inputPaths: inputPaths,
|
inputPaths: inputPaths,
|
||||||
inputFileListPaths: inputFileListPaths,
|
inputFileListPaths: inputFileListPaths,
|
||||||
outputPaths: outputPaths,
|
outputPaths: outputPaths,
|
||||||
outputFileListPaths: outputFileListPaths)
|
outputFileListPaths: outputFileListPaths,
|
||||||
|
basedOnDependencyAnalysis: basedOnDependencyAnalysis)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a target action that gets executed before the sources and resources build phase.
|
/// Returns a target action that gets executed before the sources and resources build phase.
|
||||||
|
@ -122,6 +132,7 @@ public struct TargetAction: Codable, Equatable {
|
||||||
/// - inputFileListPaths: List of input filelist paths.
|
/// - inputFileListPaths: List of input filelist paths.
|
||||||
/// - outputPaths: List of output file paths.
|
/// - outputPaths: List of output file paths.
|
||||||
/// - outputFileListPaths: List of output filelist paths.
|
/// - outputFileListPaths: List of output filelist paths.
|
||||||
|
/// - basedOnDependencyAnalysis: Whether to skip running this script in incremental builds
|
||||||
/// - Returns: Target action.
|
/// - Returns: Target action.
|
||||||
public static func pre(tool: String,
|
public static func pre(tool: String,
|
||||||
arguments: [String],
|
arguments: [String],
|
||||||
|
@ -129,7 +140,8 @@ public struct TargetAction: Codable, Equatable {
|
||||||
inputPaths: [Path] = [],
|
inputPaths: [Path] = [],
|
||||||
inputFileListPaths: [Path] = [],
|
inputFileListPaths: [Path] = [],
|
||||||
outputPaths: [Path] = [],
|
outputPaths: [Path] = [],
|
||||||
outputFileListPaths: [Path] = []) -> TargetAction
|
outputFileListPaths: [Path] = [],
|
||||||
|
basedOnDependencyAnalysis: Bool? = nil) -> TargetAction
|
||||||
{
|
{
|
||||||
TargetAction(name: name,
|
TargetAction(name: name,
|
||||||
tool: tool,
|
tool: tool,
|
||||||
|
@ -139,7 +151,8 @@ public struct TargetAction: Codable, Equatable {
|
||||||
inputPaths: inputPaths,
|
inputPaths: inputPaths,
|
||||||
inputFileListPaths: inputFileListPaths,
|
inputFileListPaths: inputFileListPaths,
|
||||||
outputPaths: outputPaths,
|
outputPaths: outputPaths,
|
||||||
outputFileListPaths: outputFileListPaths)
|
outputFileListPaths: outputFileListPaths,
|
||||||
|
basedOnDependencyAnalysis: basedOnDependencyAnalysis)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a target action that gets executed before the sources and resources build phase.
|
/// Returns a target action that gets executed before the sources and resources build phase.
|
||||||
|
@ -152,6 +165,7 @@ public struct TargetAction: Codable, Equatable {
|
||||||
/// - inputFileListPaths: List of input filelist paths.
|
/// - inputFileListPaths: List of input filelist paths.
|
||||||
/// - outputPaths: List of output file paths.
|
/// - outputPaths: List of output file paths.
|
||||||
/// - outputFileListPaths: List of output filelist paths.
|
/// - outputFileListPaths: List of output filelist paths.
|
||||||
|
/// - basedOnDependencyAnalysis: Whether to skip running this script in incremental builds
|
||||||
/// - Returns: Target action.
|
/// - Returns: Target action.
|
||||||
public static func pre(path: Path,
|
public static func pre(path: Path,
|
||||||
arguments: String...,
|
arguments: String...,
|
||||||
|
@ -159,7 +173,8 @@ public struct TargetAction: Codable, Equatable {
|
||||||
inputPaths: [Path] = [],
|
inputPaths: [Path] = [],
|
||||||
inputFileListPaths: [Path] = [],
|
inputFileListPaths: [Path] = [],
|
||||||
outputPaths: [Path] = [],
|
outputPaths: [Path] = [],
|
||||||
outputFileListPaths: [Path] = []) -> TargetAction
|
outputFileListPaths: [Path] = [],
|
||||||
|
basedOnDependencyAnalysis: Bool? = nil) -> TargetAction
|
||||||
{
|
{
|
||||||
TargetAction(name: name,
|
TargetAction(name: name,
|
||||||
tool: nil,
|
tool: nil,
|
||||||
|
@ -169,7 +184,8 @@ public struct TargetAction: Codable, Equatable {
|
||||||
inputPaths: inputPaths,
|
inputPaths: inputPaths,
|
||||||
inputFileListPaths: inputFileListPaths,
|
inputFileListPaths: inputFileListPaths,
|
||||||
outputPaths: outputPaths,
|
outputPaths: outputPaths,
|
||||||
outputFileListPaths: outputFileListPaths)
|
outputFileListPaths: outputFileListPaths,
|
||||||
|
basedOnDependencyAnalysis: basedOnDependencyAnalysis)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a target action that gets executed before the sources and resources build phase.
|
/// Returns a target action that gets executed before the sources and resources build phase.
|
||||||
|
@ -182,6 +198,7 @@ public struct TargetAction: Codable, Equatable {
|
||||||
/// - inputFileListPaths: List of input filelist paths.
|
/// - inputFileListPaths: List of input filelist paths.
|
||||||
/// - outputPaths: List of output file paths.
|
/// - outputPaths: List of output file paths.
|
||||||
/// - outputFileListPaths: List of output filelist paths.
|
/// - outputFileListPaths: List of output filelist paths.
|
||||||
|
/// - basedOnDependencyAnalysis: Whether to skip running this script in incremental builds
|
||||||
/// - Returns: Target action.
|
/// - Returns: Target action.
|
||||||
public static func pre(path: Path,
|
public static func pre(path: Path,
|
||||||
arguments: [String],
|
arguments: [String],
|
||||||
|
@ -189,7 +206,8 @@ public struct TargetAction: Codable, Equatable {
|
||||||
inputPaths: [Path] = [],
|
inputPaths: [Path] = [],
|
||||||
inputFileListPaths: [Path] = [],
|
inputFileListPaths: [Path] = [],
|
||||||
outputPaths: [Path] = [],
|
outputPaths: [Path] = [],
|
||||||
outputFileListPaths: [Path] = []) -> TargetAction
|
outputFileListPaths: [Path] = [],
|
||||||
|
basedOnDependencyAnalysis: Bool? = nil) -> TargetAction
|
||||||
{
|
{
|
||||||
TargetAction(name: name,
|
TargetAction(name: name,
|
||||||
tool: nil,
|
tool: nil,
|
||||||
|
@ -199,7 +217,8 @@ public struct TargetAction: Codable, Equatable {
|
||||||
inputPaths: inputPaths,
|
inputPaths: inputPaths,
|
||||||
inputFileListPaths: inputFileListPaths,
|
inputFileListPaths: inputFileListPaths,
|
||||||
outputPaths: outputPaths,
|
outputPaths: outputPaths,
|
||||||
outputFileListPaths: outputFileListPaths)
|
outputFileListPaths: outputFileListPaths,
|
||||||
|
basedOnDependencyAnalysis: basedOnDependencyAnalysis)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a target action that gets executed after the sources and resources build phase.
|
/// Returns a target action that gets executed after the sources and resources build phase.
|
||||||
|
@ -212,6 +231,7 @@ public struct TargetAction: Codable, Equatable {
|
||||||
/// - inputFileListPaths: List of input filelist paths.
|
/// - inputFileListPaths: List of input filelist paths.
|
||||||
/// - outputPaths: List of output file paths.
|
/// - outputPaths: List of output file paths.
|
||||||
/// - outputFileListPaths: List of output filelist paths.
|
/// - outputFileListPaths: List of output filelist paths.
|
||||||
|
/// - basedOnDependencyAnalysis: Whether to skip running this script in incremental builds
|
||||||
/// - Returns: Target action.
|
/// - Returns: Target action.
|
||||||
public static func post(tool: String,
|
public static func post(tool: String,
|
||||||
arguments: String...,
|
arguments: String...,
|
||||||
|
@ -219,7 +239,8 @@ public struct TargetAction: Codable, Equatable {
|
||||||
inputPaths: [Path] = [],
|
inputPaths: [Path] = [],
|
||||||
inputFileListPaths: [Path] = [],
|
inputFileListPaths: [Path] = [],
|
||||||
outputPaths: [Path] = [],
|
outputPaths: [Path] = [],
|
||||||
outputFileListPaths: [Path] = []) -> TargetAction
|
outputFileListPaths: [Path] = [],
|
||||||
|
basedOnDependencyAnalysis: Bool? = nil) -> TargetAction
|
||||||
{
|
{
|
||||||
TargetAction(name: name,
|
TargetAction(name: name,
|
||||||
tool: tool,
|
tool: tool,
|
||||||
|
@ -229,7 +250,8 @@ public struct TargetAction: Codable, Equatable {
|
||||||
inputPaths: inputPaths,
|
inputPaths: inputPaths,
|
||||||
inputFileListPaths: inputFileListPaths,
|
inputFileListPaths: inputFileListPaths,
|
||||||
outputPaths: outputPaths,
|
outputPaths: outputPaths,
|
||||||
outputFileListPaths: outputFileListPaths)
|
outputFileListPaths: outputFileListPaths,
|
||||||
|
basedOnDependencyAnalysis: basedOnDependencyAnalysis)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a target action that gets executed after the sources and resources build phase.
|
/// Returns a target action that gets executed after the sources and resources build phase.
|
||||||
|
@ -242,6 +264,7 @@ public struct TargetAction: Codable, Equatable {
|
||||||
/// - inputFileListPaths: List of input filelist paths.
|
/// - inputFileListPaths: List of input filelist paths.
|
||||||
/// - outputPaths: List of output file paths.
|
/// - outputPaths: List of output file paths.
|
||||||
/// - outputFileListPaths: List of output filelist paths.
|
/// - outputFileListPaths: List of output filelist paths.
|
||||||
|
/// - basedOnDependencyAnalysis: Whether to skip running this script in incremental builds
|
||||||
/// - Returns: Target action.
|
/// - Returns: Target action.
|
||||||
public static func post(tool: String,
|
public static func post(tool: String,
|
||||||
arguments: [String],
|
arguments: [String],
|
||||||
|
@ -249,7 +272,8 @@ public struct TargetAction: Codable, Equatable {
|
||||||
inputPaths: [Path] = [],
|
inputPaths: [Path] = [],
|
||||||
inputFileListPaths: [Path] = [],
|
inputFileListPaths: [Path] = [],
|
||||||
outputPaths: [Path] = [],
|
outputPaths: [Path] = [],
|
||||||
outputFileListPaths: [Path] = []) -> TargetAction
|
outputFileListPaths: [Path] = [],
|
||||||
|
basedOnDependencyAnalysis: Bool? = nil) -> TargetAction
|
||||||
{
|
{
|
||||||
TargetAction(name: name,
|
TargetAction(name: name,
|
||||||
tool: tool,
|
tool: tool,
|
||||||
|
@ -259,7 +283,8 @@ public struct TargetAction: Codable, Equatable {
|
||||||
inputPaths: inputPaths,
|
inputPaths: inputPaths,
|
||||||
inputFileListPaths: inputFileListPaths,
|
inputFileListPaths: inputFileListPaths,
|
||||||
outputPaths: outputPaths,
|
outputPaths: outputPaths,
|
||||||
outputFileListPaths: outputFileListPaths)
|
outputFileListPaths: outputFileListPaths,
|
||||||
|
basedOnDependencyAnalysis: basedOnDependencyAnalysis)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a target action that gets executed after the sources and resources build phase.
|
/// Returns a target action that gets executed after the sources and resources build phase.
|
||||||
|
@ -272,6 +297,7 @@ public struct TargetAction: Codable, Equatable {
|
||||||
/// - inputFileListPaths: List of input filelist paths.
|
/// - inputFileListPaths: List of input filelist paths.
|
||||||
/// - outputPaths: List of output file paths.
|
/// - outputPaths: List of output file paths.
|
||||||
/// - outputFileListPaths: List of output filelist paths.
|
/// - outputFileListPaths: List of output filelist paths.
|
||||||
|
/// - basedOnDependencyAnalysis: Whether to skip running this script in incremental builds
|
||||||
/// - Returns: Target action.
|
/// - Returns: Target action.
|
||||||
public static func post(path: Path,
|
public static func post(path: Path,
|
||||||
arguments: String...,
|
arguments: String...,
|
||||||
|
@ -279,7 +305,8 @@ public struct TargetAction: Codable, Equatable {
|
||||||
inputPaths: [Path] = [],
|
inputPaths: [Path] = [],
|
||||||
inputFileListPaths: [Path] = [],
|
inputFileListPaths: [Path] = [],
|
||||||
outputPaths: [Path] = [],
|
outputPaths: [Path] = [],
|
||||||
outputFileListPaths: [Path] = []) -> TargetAction
|
outputFileListPaths: [Path] = [],
|
||||||
|
basedOnDependencyAnalysis: Bool? = nil) -> TargetAction
|
||||||
{
|
{
|
||||||
TargetAction(name: name,
|
TargetAction(name: name,
|
||||||
tool: nil,
|
tool: nil,
|
||||||
|
@ -289,7 +316,8 @@ public struct TargetAction: Codable, Equatable {
|
||||||
inputPaths: inputPaths,
|
inputPaths: inputPaths,
|
||||||
inputFileListPaths: inputFileListPaths,
|
inputFileListPaths: inputFileListPaths,
|
||||||
outputPaths: outputPaths,
|
outputPaths: outputPaths,
|
||||||
outputFileListPaths: outputFileListPaths)
|
outputFileListPaths: outputFileListPaths,
|
||||||
|
basedOnDependencyAnalysis: basedOnDependencyAnalysis)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a target action that gets executed after the sources and resources build phase.
|
/// Returns a target action that gets executed after the sources and resources build phase.
|
||||||
|
@ -302,6 +330,7 @@ public struct TargetAction: Codable, Equatable {
|
||||||
/// - inputFileListPaths: List of input filelist paths.
|
/// - inputFileListPaths: List of input filelist paths.
|
||||||
/// - outputPaths: List of output file paths.
|
/// - outputPaths: List of output file paths.
|
||||||
/// - outputFileListPaths: List of output filelist paths.
|
/// - outputFileListPaths: List of output filelist paths.
|
||||||
|
/// - basedOnDependencyAnalysis: Whether to skip running this script in incremental builds
|
||||||
/// - Returns: Target action.
|
/// - Returns: Target action.
|
||||||
public static func post(path: Path,
|
public static func post(path: Path,
|
||||||
arguments: [String],
|
arguments: [String],
|
||||||
|
@ -309,7 +338,8 @@ public struct TargetAction: Codable, Equatable {
|
||||||
inputPaths: [Path] = [],
|
inputPaths: [Path] = [],
|
||||||
inputFileListPaths: [Path] = [],
|
inputFileListPaths: [Path] = [],
|
||||||
outputPaths: [Path] = [],
|
outputPaths: [Path] = [],
|
||||||
outputFileListPaths: [Path] = []) -> TargetAction
|
outputFileListPaths: [Path] = [],
|
||||||
|
basedOnDependencyAnalysis: Bool? = nil) -> TargetAction
|
||||||
{
|
{
|
||||||
TargetAction(name: name,
|
TargetAction(name: name,
|
||||||
tool: nil,
|
tool: nil,
|
||||||
|
@ -319,7 +349,8 @@ public struct TargetAction: Codable, Equatable {
|
||||||
inputPaths: inputPaths,
|
inputPaths: inputPaths,
|
||||||
inputFileListPaths: inputFileListPaths,
|
inputFileListPaths: inputFileListPaths,
|
||||||
outputPaths: outputPaths,
|
outputPaths: outputPaths,
|
||||||
outputFileListPaths: outputFileListPaths)
|
outputFileListPaths: outputFileListPaths,
|
||||||
|
basedOnDependencyAnalysis: basedOnDependencyAnalysis)
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Codable
|
// MARK: - Codable
|
||||||
|
@ -333,6 +364,7 @@ public struct TargetAction: Codable, Equatable {
|
||||||
inputFileListPaths = try container.decodeIfPresent([Path].self, forKey: .inputFileListPaths) ?? []
|
inputFileListPaths = try container.decodeIfPresent([Path].self, forKey: .inputFileListPaths) ?? []
|
||||||
outputPaths = try container.decodeIfPresent([Path].self, forKey: .outputPaths) ?? []
|
outputPaths = try container.decodeIfPresent([Path].self, forKey: .outputPaths) ?? []
|
||||||
outputFileListPaths = try container.decodeIfPresent([Path].self, forKey: .outputFileListPaths) ?? []
|
outputFileListPaths = try container.decodeIfPresent([Path].self, forKey: .outputFileListPaths) ?? []
|
||||||
|
basedOnDependencyAnalysis = try container.decodeIfPresent(Bool.self, forKey: .basedOnDependencyAnalysis)
|
||||||
if let path = try container.decodeIfPresent(Path.self, forKey: .path) {
|
if let path = try container.decodeIfPresent(Path.self, forKey: .path) {
|
||||||
self.path = path
|
self.path = path
|
||||||
tool = nil
|
tool = nil
|
||||||
|
@ -352,6 +384,7 @@ public struct TargetAction: Codable, Equatable {
|
||||||
try container.encode(inputFileListPaths, forKey: .inputFileListPaths)
|
try container.encode(inputFileListPaths, forKey: .inputFileListPaths)
|
||||||
try container.encode(outputPaths, forKey: .outputPaths)
|
try container.encode(outputPaths, forKey: .outputPaths)
|
||||||
try container.encode(outputFileListPaths, forKey: .outputFileListPaths)
|
try container.encode(outputFileListPaths, forKey: .outputFileListPaths)
|
||||||
|
try container.encode(basedOnDependencyAnalysis, forKey: .basedOnDependencyAnalysis)
|
||||||
|
|
||||||
if let tool = tool {
|
if let tool = tool {
|
||||||
try container.encode(tool, forKey: .tool)
|
try container.encode(tool, forKey: .tool)
|
||||||
|
|
|
@ -1,9 +0,0 @@
|
||||||
import Foundation
|
|
||||||
|
|
||||||
struct SimulatorDeviceAndRuntime: Hashable {
|
|
||||||
/// Device
|
|
||||||
let device: SimulatorDevice
|
|
||||||
|
|
||||||
/// Device's runtime.
|
|
||||||
let runtime: SimulatorRuntime
|
|
||||||
}
|
|
|
@ -19,6 +19,12 @@ public protocol BuildGraphInspecting {
|
||||||
/// - graph: Dependency graph.
|
/// - graph: Dependency graph.
|
||||||
func buildableTarget(scheme: Scheme, graph: Graph) -> Target?
|
func buildableTarget(scheme: Scheme, graph: Graph) -> Target?
|
||||||
|
|
||||||
|
/// From the list of testable targets of the given scheme, it returns the first one.
|
||||||
|
/// - Parameters:
|
||||||
|
/// - scheme: Scheme in which to look up the target.
|
||||||
|
/// - graph: Dependency graph.
|
||||||
|
func testableTarget(scheme: Scheme, graph: Graph) -> Target?
|
||||||
|
|
||||||
/// Given a graph, it returns a list of buildable schemes.
|
/// Given a graph, it returns a list of buildable schemes.
|
||||||
/// - Parameter graph: Dependency graph.
|
/// - Parameter graph: Dependency graph.
|
||||||
func buildableSchemes(graph: Graph) -> [Scheme]
|
func buildableSchemes(graph: Graph) -> [Scheme]
|
||||||
|
@ -27,6 +33,14 @@ public protocol BuildGraphInspecting {
|
||||||
/// - Parameters:
|
/// - Parameters:
|
||||||
/// - graph: Dependency graph
|
/// - graph: Dependency graph
|
||||||
func buildableEntrySchemes(graph: Graph) -> [Scheme]
|
func buildableEntrySchemes(graph: Graph) -> [Scheme]
|
||||||
|
|
||||||
|
/// Given a graph, it returns a list of test schemes (those that include only one test target).
|
||||||
|
/// - Parameter graph: Dependency graph.
|
||||||
|
func testSchemes(graph: Graph) -> [Scheme]
|
||||||
|
|
||||||
|
/// Given a graph, it returns a list of testable schemes.
|
||||||
|
/// - Parameter graph: Dependency graph.
|
||||||
|
func testableSchemes(graph: Graph) -> [Scheme]
|
||||||
}
|
}
|
||||||
|
|
||||||
public class BuildGraphInspector: BuildGraphInspecting {
|
public class BuildGraphInspector: BuildGraphInspecting {
|
||||||
|
@ -53,17 +67,26 @@ public class BuildGraphInspector: BuildGraphInspecting {
|
||||||
}
|
}
|
||||||
|
|
||||||
public func buildableTarget(scheme: Scheme, graph: Graph) -> Target? {
|
public func buildableTarget(scheme: Scheme, graph: Graph) -> Target? {
|
||||||
if scheme.buildAction?.targets.count == 0 {
|
guard
|
||||||
|
scheme.buildAction?.targets.isEmpty == false,
|
||||||
|
let buildTarget = scheme.buildAction?.targets.first
|
||||||
|
else {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
let buildTarget = scheme.buildAction!.targets.first!
|
|
||||||
return graph.target(path: buildTarget.projectPath, name: buildTarget.name)!.target
|
return graph.target(path: buildTarget.projectPath, name: buildTarget.name)?.target
|
||||||
|
}
|
||||||
|
|
||||||
|
public func testableTarget(scheme: Scheme, graph: Graph) -> Target? {
|
||||||
|
if scheme.testAction?.targets.count == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
let testTarget = scheme.testAction!.targets.first!
|
||||||
|
return graph.target(path: testTarget.target.projectPath, name: testTarget.target.name)!.target
|
||||||
}
|
}
|
||||||
|
|
||||||
public func buildableSchemes(graph: Graph) -> [Scheme] {
|
public func buildableSchemes(graph: Graph) -> [Scheme] {
|
||||||
graph.targets.values.flatMap {
|
graph.schemes
|
||||||
$0.flatMap { $0.project.schemes }
|
|
||||||
}
|
|
||||||
.filter { $0.buildAction?.targets.isEmpty == false }
|
.filter { $0.buildAction?.targets.isEmpty == false }
|
||||||
.sorted(by: { $0.name < $1.name })
|
.sorted(by: { $0.name < $1.name })
|
||||||
}
|
}
|
||||||
|
@ -76,6 +99,25 @@ public class BuildGraphInspector: BuildGraphInspecting {
|
||||||
.sorted(by: { $0.name < $1.name })
|
.sorted(by: { $0.name < $1.name })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public func testableSchemes(graph: Graph) -> [Scheme] {
|
||||||
|
graph.schemes
|
||||||
|
.filter { $0.testAction?.targets.isEmpty == false }
|
||||||
|
.sorted(by: { $0.name < $1.name })
|
||||||
|
}
|
||||||
|
|
||||||
|
public func testSchemes(graph: Graph) -> [Scheme] {
|
||||||
|
graph.targets.values.flatMap { target -> [Scheme] in
|
||||||
|
target
|
||||||
|
.filter { $0.target.product == .unitTests || $0.target.product == .uiTests }
|
||||||
|
.flatMap { target -> [Scheme] in
|
||||||
|
target.project.schemes
|
||||||
|
.filter { $0.targetDependencies().map(\.name) == [target.name] }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.filter { $0.testAction?.targets.isEmpty == false }
|
||||||
|
.sorted(by: { $0.name < $1.name })
|
||||||
|
}
|
||||||
|
|
||||||
public func workspacePath(directory: AbsolutePath) throws -> AbsolutePath? {
|
public func workspacePath(directory: AbsolutePath) throws -> AbsolutePath? {
|
||||||
try directory.glob("**/*.xcworkspace")
|
try directory.glob("**/*.xcworkspace")
|
||||||
.filter {
|
.filter {
|
||||||
|
|
|
@ -53,6 +53,40 @@ public final class XcodeBuildController: XcodeBuildControlling {
|
||||||
return run(command: command)
|
return run(command: command)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public func test(
|
||||||
|
_ target: XcodeBuildTarget,
|
||||||
|
scheme: String,
|
||||||
|
clean: Bool = false,
|
||||||
|
destination: XcodeBuildDestination,
|
||||||
|
arguments: [XcodeBuildArgument]
|
||||||
|
) -> Observable<SystemEvent<XcodeBuildOutput>> {
|
||||||
|
var command = ["/usr/bin/xcrun", "xcodebuild"]
|
||||||
|
|
||||||
|
// Action
|
||||||
|
if clean {
|
||||||
|
command.append("clean")
|
||||||
|
}
|
||||||
|
command.append("test")
|
||||||
|
|
||||||
|
// Scheme
|
||||||
|
command.append(contentsOf: ["-scheme", scheme])
|
||||||
|
|
||||||
|
// Target
|
||||||
|
command.append(contentsOf: target.xcodebuildArguments)
|
||||||
|
|
||||||
|
// Arguments
|
||||||
|
command.append(contentsOf: arguments.flatMap { $0.arguments })
|
||||||
|
|
||||||
|
switch destination {
|
||||||
|
case let .device(udid):
|
||||||
|
command.append(contentsOf: ["-destination", "id=\(udid)"])
|
||||||
|
case .mac:
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
return run(command: command)
|
||||||
|
}
|
||||||
|
|
||||||
public func archive(_ target: XcodeBuildTarget,
|
public func archive(_ target: XcodeBuildTarget,
|
||||||
scheme: String,
|
scheme: String,
|
||||||
clean: Bool,
|
clean: Bool,
|
||||||
|
@ -168,29 +202,18 @@ public final class XcodeBuildController: XcodeBuildControlling {
|
||||||
switch event {
|
switch event {
|
||||||
case let .standardError(errorData):
|
case let .standardError(errorData):
|
||||||
guard let line = String(data: errorData, encoding: .utf8) else { return Observable.empty() }
|
guard let line = String(data: errorData, encoding: .utf8) else { return Observable.empty() }
|
||||||
return Observable.create { observer in
|
let output = line.split(separator: "\n").map { line -> SystemEvent<XcodeBuildOutput> in
|
||||||
let lines = line.split(separator: "\n")
|
|
||||||
lines.map { line in
|
|
||||||
let formatedOutput = self.parser.parse(line: String(line), colored: colored)
|
let formatedOutput = self.parser.parse(line: String(line), colored: colored)
|
||||||
return SystemEvent.standardError(XcodeBuildOutput(raw: "\(String(line))\n", formatted: formatedOutput.map { "\($0)\n" }))
|
return SystemEvent.standardError(XcodeBuildOutput(raw: "\(String(line))\n", formatted: formatedOutput.map { "\($0)\n" }))
|
||||||
}
|
}
|
||||||
.forEach(observer.onNext)
|
return Observable.from(output)
|
||||||
observer.onCompleted()
|
|
||||||
return Disposables.create()
|
|
||||||
}
|
|
||||||
case let .standardOutput(outputData):
|
case let .standardOutput(outputData):
|
||||||
guard let line = String(data: outputData, encoding: .utf8) else { return Observable.empty() }
|
guard let line = String(data: outputData, encoding: .utf8) else { return Observable.empty() }
|
||||||
|
let output = line.split(separator: "\n").map { line -> SystemEvent<XcodeBuildOutput> in
|
||||||
return Observable.create { observer in
|
|
||||||
let lines = line.split(separator: "\n")
|
|
||||||
lines.map { line in
|
|
||||||
let formatedOutput = self.parser.parse(line: String(line), colored: colored)
|
let formatedOutput = self.parser.parse(line: String(line), colored: colored)
|
||||||
return SystemEvent.standardOutput(XcodeBuildOutput(raw: "\(String(line))\n", formatted: formatedOutput.map { "\($0)\n" }))
|
return SystemEvent.standardOutput(XcodeBuildOutput(raw: "\(String(line))\n", formatted: formatedOutput.map { "\($0)\n" }))
|
||||||
}
|
}
|
||||||
.forEach(observer.onNext)
|
return Observable.from(output)
|
||||||
observer.onCompleted()
|
|
||||||
return Disposables.create()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -43,4 +43,27 @@ public final class MockBuildGraphInspector: BuildGraphInspecting {
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public var testableTargetStub: ((Scheme, Graph) -> Target?)?
|
||||||
|
public func testableTarget(scheme: Scheme, graph: Graph) -> Target? {
|
||||||
|
if let testableTargetStub = testableTargetStub {
|
||||||
|
return testableTargetStub(scheme, graph)
|
||||||
|
} else {
|
||||||
|
return Target.test()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public var testableSchemesStub: ((Graph) -> [Scheme])?
|
||||||
|
public func testableSchemes(graph: Graph) -> [Scheme] {
|
||||||
|
if let testableSchemesStub = testableSchemesStub {
|
||||||
|
return testableSchemesStub(graph)
|
||||||
|
} else {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public var testSchemesStub: ((Graph) -> [Scheme])?
|
||||||
|
public func testSchemes(graph: Graph) -> [Scheme] {
|
||||||
|
testSchemesStub?(graph) ?? []
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -48,8 +48,8 @@ public final class Cache: CacheStoring {
|
||||||
}!
|
}!
|
||||||
}
|
}
|
||||||
|
|
||||||
public func store(hash: String, xcframeworkPath: AbsolutePath) -> Completable {
|
public func store(hash: String, paths: [AbsolutePath]) -> Completable {
|
||||||
let storages = storageProvider.storages()
|
let storages = storageProvider.storages()
|
||||||
return Completable.zip(storages.map { $0.store(hash: hash, xcframeworkPath: xcframeworkPath) })
|
return Completable.zip(storages.map { $0.store(hash: hash, paths: paths) })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,7 +29,7 @@ public final class CacheLocalStorage: CacheStoring {
|
||||||
// MARK: - Init
|
// MARK: - Init
|
||||||
|
|
||||||
public convenience init() {
|
public convenience init() {
|
||||||
self.init(cacheDirectory: Environment.shared.xcframeworksCacheDirectory)
|
self.init(cacheDirectory: Environment.shared.buildCacheDirectory)
|
||||||
}
|
}
|
||||||
|
|
||||||
init(cacheDirectory: AbsolutePath) {
|
init(cacheDirectory: AbsolutePath) {
|
||||||
|
@ -40,14 +40,14 @@ public final class CacheLocalStorage: CacheStoring {
|
||||||
|
|
||||||
public func exists(hash: String) -> Single<Bool> {
|
public func exists(hash: String) -> Single<Bool> {
|
||||||
Single.create { (completed) -> Disposable in
|
Single.create { (completed) -> Disposable in
|
||||||
completed(.success(FileHandler.shared.glob(self.cacheDirectory, glob: "\(hash)/*").count != 0))
|
completed(.success(self.lookupFramework(directory: self.cacheDirectory.appending(component: hash)) != nil))
|
||||||
return Disposables.create()
|
return Disposables.create()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public func fetch(hash: String) -> Single<AbsolutePath> {
|
public func fetch(hash: String) -> Single<AbsolutePath> {
|
||||||
Single.create { (completed) -> Disposable in
|
Single.create { (completed) -> Disposable in
|
||||||
if let path = FileHandler.shared.glob(self.cacheDirectory, glob: "\(hash)/*").first {
|
if let path = self.lookupFramework(directory: self.cacheDirectory.appending(component: hash)) {
|
||||||
completed(.success(path))
|
completed(.success(path))
|
||||||
} else {
|
} else {
|
||||||
completed(.error(CacheLocalStorageError.xcframeworkNotFound(hash: hash)))
|
completed(.error(CacheLocalStorageError.xcframeworkNotFound(hash: hash)))
|
||||||
|
@ -56,21 +56,22 @@ public final class CacheLocalStorage: CacheStoring {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public func store(hash: String, xcframeworkPath: AbsolutePath) -> Completable {
|
public func store(hash: String, paths: [AbsolutePath]) -> Completable {
|
||||||
let copy = Completable.create { (completed) -> Disposable in
|
let copy = Completable.create { (completed) -> Disposable in
|
||||||
let hashFolder = self.cacheDirectory.appending(component: hash)
|
let hashFolder = self.cacheDirectory.appending(component: hash)
|
||||||
let destinationPath = hashFolder.appending(component: xcframeworkPath.basename)
|
|
||||||
|
|
||||||
do {
|
do {
|
||||||
if !FileHandler.shared.exists(hashFolder) {
|
if !FileHandler.shared.exists(hashFolder) {
|
||||||
try FileHandler.shared.createFolder(hashFolder)
|
try FileHandler.shared.createFolder(hashFolder)
|
||||||
}
|
}
|
||||||
|
try paths.forEach { sourcePath in
|
||||||
|
let destinationPath = hashFolder.appending(component: sourcePath.basename)
|
||||||
if FileHandler.shared.exists(destinationPath) {
|
if FileHandler.shared.exists(destinationPath) {
|
||||||
try FileHandler.shared.delete(destinationPath)
|
try FileHandler.shared.delete(destinationPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
try FileHandler.shared.copy(from: xcframeworkPath, to: destinationPath)
|
try FileHandler.shared.copy(from: sourcePath, to: destinationPath)
|
||||||
|
}
|
||||||
} catch {
|
} catch {
|
||||||
completed(.error(error))
|
completed(.error(error))
|
||||||
return Disposables.create()
|
return Disposables.create()
|
||||||
|
@ -84,6 +85,14 @@ public final class CacheLocalStorage: CacheStoring {
|
||||||
|
|
||||||
// MARK: - Fileprivate
|
// MARK: - Fileprivate
|
||||||
|
|
||||||
|
fileprivate func lookupFramework(directory: AbsolutePath) -> AbsolutePath? {
|
||||||
|
let extensions = ["framework", "xcframework"]
|
||||||
|
for ext in extensions {
|
||||||
|
if let filePath = FileHandler.shared.glob(directory, glob: "*.\(ext)").first { return filePath }
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
fileprivate func createCacheDirectory() -> Completable {
|
fileprivate func createCacheDirectory() -> Completable {
|
||||||
Completable.create { (completed) -> Disposable in
|
Completable.create { (completed) -> Disposable in
|
||||||
do {
|
do {
|
||||||
|
|
|
@ -5,18 +5,18 @@ import TuistCore
|
||||||
import TuistSupport
|
import TuistSupport
|
||||||
|
|
||||||
enum CacheRemoteStorageError: FatalError, Equatable {
|
enum CacheRemoteStorageError: FatalError, Equatable {
|
||||||
case archiveDoesNotContainXCFramework(AbsolutePath)
|
case frameworkNotFound(hash: String)
|
||||||
|
|
||||||
var type: ErrorType {
|
var type: ErrorType {
|
||||||
switch self {
|
switch self {
|
||||||
case .archiveDoesNotContainXCFramework: return .abort
|
case .frameworkNotFound: return .abort
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var description: String {
|
var description: String {
|
||||||
switch self {
|
switch self {
|
||||||
case let .archiveDoesNotContainXCFramework(path):
|
case let .frameworkNotFound(hash):
|
||||||
return "Unzipped archive at path \(path.pathString) does not contain any xcframework."
|
return "The downloaded artifact with hash '\(hash)' has an incorrect format and doesn't contain a xcframework nor a framework."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -28,21 +28,20 @@ public final class CacheRemoteStorage: CacheStoring {
|
||||||
private let cloudConfig: Cloud
|
private let cloudConfig: Cloud
|
||||||
private let cloudClient: CloudClienting
|
private let cloudClient: CloudClienting
|
||||||
private let fileClient: FileClienting
|
private let fileClient: FileClienting
|
||||||
private let fileArchiverFactory: FileArchiverManufacturing
|
private let fileArchiverFactory: FileArchivingFactorying
|
||||||
private var fileArchiverMap: [AbsolutePath: FileArchiving] = [:]
|
|
||||||
|
|
||||||
// MARK: - Init
|
// MARK: - Init
|
||||||
|
|
||||||
public convenience init(cloudConfig: Cloud, cloudClient: CloudClienting) {
|
public convenience init(cloudConfig: Cloud, cloudClient: CloudClienting) {
|
||||||
self.init(cloudConfig: cloudConfig,
|
self.init(cloudConfig: cloudConfig,
|
||||||
cloudClient: cloudClient,
|
cloudClient: cloudClient,
|
||||||
fileArchiverFactory: FileArchiverFactory(),
|
fileArchiverFactory: FileArchivingFactory(),
|
||||||
fileClient: FileClient())
|
fileClient: FileClient())
|
||||||
}
|
}
|
||||||
|
|
||||||
init(cloudConfig: Cloud,
|
init(cloudConfig: Cloud,
|
||||||
cloudClient: CloudClienting,
|
cloudClient: CloudClienting,
|
||||||
fileArchiverFactory: FileArchiverManufacturing,
|
fileArchiverFactory: FileArchivingFactorying,
|
||||||
fileClient: FileClienting)
|
fileClient: FileClienting)
|
||||||
{
|
{
|
||||||
self.cloudConfig = cloudConfig
|
self.cloudConfig = cloudConfig
|
||||||
|
@ -79,7 +78,10 @@ public final class CacheRemoteStorage: CacheStoring {
|
||||||
return cloudClient
|
return cloudClient
|
||||||
.request(resource)
|
.request(resource)
|
||||||
.map { $0.object.data.url }
|
.map { $0.object.data.url }
|
||||||
.flatMap { (url: URL) in self.fileClient.download(url: url) }
|
.flatMap { (url: URL) in
|
||||||
|
self.fileClient.download(url: url)
|
||||||
|
.do(onSubscribed: { logger.info("Downloading cache artifact with hash \(hash).") })
|
||||||
|
}
|
||||||
.flatMap { (filePath: AbsolutePath) in
|
.flatMap { (filePath: AbsolutePath) in
|
||||||
do {
|
do {
|
||||||
let archiveContentPath = try self.unzip(downloadedArchive: filePath, hash: hash)
|
let archiveContentPath = try self.unzip(downloadedArchive: filePath, hash: hash)
|
||||||
|
@ -93,10 +95,10 @@ public final class CacheRemoteStorage: CacheStoring {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public func store(hash: String, xcframeworkPath: AbsolutePath) -> Completable {
|
public func store(hash: String, paths: [AbsolutePath]) -> Completable {
|
||||||
do {
|
do {
|
||||||
let archiver = fileArchiver(for: xcframeworkPath)
|
let archiver = try fileArchiverFactory.makeFileArchiver(for: paths)
|
||||||
let destinationZipPath = try archiver.zip()
|
let destinationZipPath = try archiver.zip(name: hash)
|
||||||
let resource = try CloudCacheResponse.storeResource(
|
let resource = try CloudCacheResponse.storeResource(
|
||||||
hash: hash,
|
hash: hash,
|
||||||
cloud: cloudConfig,
|
cloud: cloudConfig,
|
||||||
|
@ -119,26 +121,31 @@ public final class CacheRemoteStorage: CacheStoring {
|
||||||
|
|
||||||
// MARK: - Private
|
// MARK: - Private
|
||||||
|
|
||||||
private func xcframeworkPath(in archive: AbsolutePath) throws -> AbsolutePath? {
|
private func frameworkPath(in archive: AbsolutePath) -> AbsolutePath? {
|
||||||
let folderContent = try FileHandler.shared.contentsOfDirectory(archive)
|
if let xcframeworkPath = FileHandler.shared.glob(archive, glob: "*.xcframework").first {
|
||||||
return folderContent.filter { FileHandler.shared.isFolder($0) && $0.extension == "xcframework" }.first
|
return xcframeworkPath
|
||||||
|
} else if let frameworkPath = FileHandler.shared.glob(archive, glob: "*.framework").first {
|
||||||
|
return frameworkPath
|
||||||
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
private func unzip(downloadedArchive: AbsolutePath, hash: String) throws -> AbsolutePath {
|
private func unzip(downloadedArchive: AbsolutePath, hash: String) throws -> AbsolutePath {
|
||||||
let zipPath = try FileHandler.shared.changeExtension(path: downloadedArchive, to: "zip")
|
let zipPath = try FileHandler.shared.changeExtension(path: downloadedArchive, to: "zip")
|
||||||
let archiveDestination = Environment.shared.xcframeworksCacheDirectory.appending(component: hash)
|
let archiveDestination = Environment.shared.buildCacheDirectory.appending(component: hash)
|
||||||
try fileArchiver(for: zipPath).unzip(to: archiveDestination)
|
let fileUnarchiver = try fileArchiverFactory.makeFileUnarchiver(for: zipPath)
|
||||||
guard let xcframework = try xcframeworkPath(in: archiveDestination) else {
|
let unarchivedDirectory = try fileUnarchiver.unzip()
|
||||||
try FileHandler.shared.delete(archiveDestination)
|
defer {
|
||||||
throw CacheRemoteStorageError.archiveDoesNotContainXCFramework(archiveDestination)
|
try? fileUnarchiver.delete()
|
||||||
}
|
}
|
||||||
return xcframework
|
if frameworkPath(in: unarchivedDirectory) == nil {
|
||||||
|
throw CacheRemoteStorageError.frameworkNotFound(hash: hash)
|
||||||
}
|
}
|
||||||
|
if !FileHandler.shared.exists(archiveDestination.parentDirectory) {
|
||||||
private func fileArchiver(for path: AbsolutePath) -> FileArchiving {
|
try FileHandler.shared.createFolder(archiveDestination.parentDirectory)
|
||||||
let fileArchiver = fileArchiverMap[path] ?? fileArchiverFactory.makeFileArchiver(for: path)
|
}
|
||||||
fileArchiverMap[path] = fileArchiver
|
try FileHandler.shared.move(from: unarchivedDirectory, to: archiveDestination)
|
||||||
return fileArchiver
|
return frameworkPath(in: archiveDestination)!
|
||||||
}
|
}
|
||||||
|
|
||||||
private func deleteZipArchiveCompletable(archiver: FileArchiving) -> Completable {
|
private func deleteZipArchiveCompletable(archiver: FileArchiving) -> Completable {
|
||||||
|
@ -152,12 +159,4 @@ public final class CacheRemoteStorage: CacheStoring {
|
||||||
return Disposables.create {}
|
return Disposables.create {}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Deinit
|
|
||||||
|
|
||||||
deinit {
|
|
||||||
do {
|
|
||||||
try fileArchiverMap.values.forEach { fileArchiver in try fileArchiver.delete() }
|
|
||||||
} catch {}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,6 +21,6 @@ public protocol CacheStoring {
|
||||||
/// It stores the xcframework at the given path in the cache.
|
/// It stores the xcframework at the given path in the cache.
|
||||||
/// - Parameters:
|
/// - Parameters:
|
||||||
/// - hash: Hash of the target the xcframework belongs to.
|
/// - hash: Hash of the target the xcframework belongs to.
|
||||||
/// - xcframeworkPath: Path to the .xcframework.
|
/// - paths: Path to the files that will be stored.
|
||||||
func store(hash: String, xcframeworkPath: AbsolutePath) -> Completable
|
func store(hash: String, paths: [AbsolutePath]) -> Completable
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,7 +16,7 @@ public final class DependenciesContentHasher: DependenciesContentHashing {
|
||||||
self.contentHasher = contentHasher
|
self.contentHasher = contentHasher
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - HeadersContentHashing
|
// MARK: - DependenciesContentHashing
|
||||||
|
|
||||||
public func hash(dependencies: [Dependency]) throws -> String {
|
public func hash(dependencies: [Dependency]) throws -> String {
|
||||||
let hashes = dependencies.map { try? hash(dependency: $0) }
|
let hashes = dependencies.map { try? hash(dependency: $0) }
|
||||||
|
|
|
@ -5,7 +5,7 @@ import TuistCore
|
||||||
import TuistSupport
|
import TuistSupport
|
||||||
|
|
||||||
public protocol GraphContentHashing {
|
public protocol GraphContentHashing {
|
||||||
func contentHashes(for graph: TuistCore.Graph) throws -> [TargetNode: String]
|
func contentHashes(for graph: TuistCore.Graph, cacheOutputType: CacheOutputType) throws -> [TargetNode: String]
|
||||||
}
|
}
|
||||||
|
|
||||||
/// `GraphContentHasher`
|
/// `GraphContentHasher`
|
||||||
|
@ -24,7 +24,7 @@ public final class GraphContentHasher: GraphContentHashing {
|
||||||
|
|
||||||
// MARK: - GraphContentHashing
|
// MARK: - GraphContentHashing
|
||||||
|
|
||||||
public func contentHashes(for graph: TuistCore.Graph) throws -> [TargetNode: String] {
|
public func contentHashes(for graph: TuistCore.Graph, cacheOutputType: CacheOutputType) throws -> [TargetNode: String] {
|
||||||
var visitedNodes: [TargetNode: Bool] = [:]
|
var visitedNodes: [TargetNode: Bool] = [:]
|
||||||
let hashableTargets = graph.targets.values.flatMap { (targets: [TargetNode]) -> [TargetNode] in
|
let hashableTargets = graph.targets.values.flatMap { (targets: [TargetNode]) -> [TargetNode] in
|
||||||
targets.compactMap { target in
|
targets.compactMap { target in
|
||||||
|
@ -35,7 +35,8 @@ public final class GraphContentHasher: GraphContentHashing {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let hashes = try hashableTargets.map {
|
let hashes = try hashableTargets.map {
|
||||||
try targetContentHasher.contentHash(for: $0)
|
try targetContentHasher.contentHash(for: $0,
|
||||||
|
cacheOutputType: cacheOutputType)
|
||||||
}
|
}
|
||||||
return Dictionary(uniqueKeysWithValues: zip(hashableTargets, hashes))
|
return Dictionary(uniqueKeysWithValues: zip(hashableTargets, hashes))
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,6 +16,8 @@ public final class ResourcesContentHasher: ResourcesContentHashing {
|
||||||
self.contentHasher = contentHasher
|
self.contentHasher = contentHasher
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MARK: - ResourcesContentHashing
|
||||||
|
|
||||||
public func hash(resources: [FileElement]) throws -> String {
|
public func hash(resources: [FileElement]) throws -> String {
|
||||||
let hashes = try resources.map { try contentHasher.hash(path: $0.path) }
|
let hashes = try resources.map { try contentHasher.hash(path: $0.path) }
|
||||||
return try contentHasher.hash(hashes)
|
return try contentHasher.hash(hashes)
|
||||||
|
|
|
@ -16,10 +16,10 @@ public final class SettingsContentHasher: SettingsContentHashing {
|
||||||
self.contentHasher = contentHasher
|
self.contentHasher = contentHasher
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - InfoPlistContentHashing
|
// MARK: - SettingsContentHashing
|
||||||
|
|
||||||
public func hash(settings: Settings) throws -> String {
|
public func hash(settings: Settings) throws -> String {
|
||||||
let baseSettingsHash = hash(settings.base)
|
let baseSettingsHash = try hash(settings.base)
|
||||||
let configurationHash = try hash(settings.configurations)
|
let configurationHash = try hash(settings.configurations)
|
||||||
let defaultSettingsHash = try hash(settings.defaultSettings)
|
let defaultSettingsHash = try hash(settings.defaultSettings)
|
||||||
return try contentHasher.hash([baseSettingsHash, configurationHash, defaultSettingsHash])
|
return try contentHasher.hash([baseSettingsHash, configurationHash, defaultSettingsHash])
|
||||||
|
@ -39,12 +39,15 @@ public final class SettingsContentHasher: SettingsContentHashing {
|
||||||
return try contentHasher.hash(configurationHashes)
|
return try contentHasher.hash(configurationHashes)
|
||||||
}
|
}
|
||||||
|
|
||||||
private func hash(_ settingsDictionary: SettingsDictionary) -> String {
|
private func hash(_ settingsDictionary: SettingsDictionary) throws -> String {
|
||||||
settingsDictionary.map { "\($0):\($1.normalize())" }.joined(separator: "-")
|
let sortedAndNormalizedSettings = settingsDictionary
|
||||||
|
.sorted(by: { $0.0 < $1.0 })
|
||||||
|
.map { "\($0):\($1.normalize())" }.joined(separator: "-")
|
||||||
|
return try contentHasher.hash(sortedAndNormalizedSettings)
|
||||||
}
|
}
|
||||||
|
|
||||||
private func hash(_ configuration: Configuration) throws -> String {
|
private func hash(_ configuration: Configuration) throws -> String {
|
||||||
var configurationHash = hash(configuration.settings)
|
var configurationHash = try hash(configuration.settings)
|
||||||
if let xcconfigPath = configuration.xcconfig {
|
if let xcconfigPath = configuration.xcconfig {
|
||||||
let xcconfigHash = try contentHasher.hash(path: xcconfigPath)
|
let xcconfigHash = try contentHasher.hash(path: xcconfigPath)
|
||||||
configurationHash += xcconfigHash
|
configurationHash += xcconfigHash
|
||||||
|
|
|
@ -4,7 +4,7 @@ import TuistCore
|
||||||
import TuistSupport
|
import TuistSupport
|
||||||
|
|
||||||
public protocol TargetContentHashing {
|
public protocol TargetContentHashing {
|
||||||
func contentHash(for target: TargetNode) throws -> String
|
func contentHash(for target: TargetNode, cacheOutputType: CacheOutputType) throws -> String
|
||||||
}
|
}
|
||||||
|
|
||||||
/// `TargetContentHasher`
|
/// `TargetContentHasher`
|
||||||
|
@ -14,6 +14,7 @@ public final class TargetContentHasher: TargetContentHashing {
|
||||||
private let coreDataModelsContentHasher: CoreDataModelsContentHashing
|
private let coreDataModelsContentHasher: CoreDataModelsContentHashing
|
||||||
private let sourceFilesContentHasher: SourceFilesContentHashing
|
private let sourceFilesContentHasher: SourceFilesContentHashing
|
||||||
private let targetActionsContentHasher: TargetActionsContentHashing
|
private let targetActionsContentHasher: TargetActionsContentHashing
|
||||||
|
private let targetScriptsContentHasher: TargetScriptsContentHashing
|
||||||
private let resourcesContentHasher: ResourcesContentHashing
|
private let resourcesContentHasher: ResourcesContentHashing
|
||||||
private let headersContentHasher: HeadersContentHashing
|
private let headersContentHasher: HeadersContentHashing
|
||||||
private let deploymentTargetContentHasher: DeploymentTargetContentHashing
|
private let deploymentTargetContentHasher: DeploymentTargetContentHashing
|
||||||
|
@ -28,6 +29,7 @@ public final class TargetContentHasher: TargetContentHashing {
|
||||||
contentHasher: contentHasher,
|
contentHasher: contentHasher,
|
||||||
sourceFilesContentHasher: SourceFilesContentHasher(contentHasher: contentHasher),
|
sourceFilesContentHasher: SourceFilesContentHasher(contentHasher: contentHasher),
|
||||||
targetActionsContentHasher: TargetActionsContentHasher(contentHasher: contentHasher),
|
targetActionsContentHasher: TargetActionsContentHasher(contentHasher: contentHasher),
|
||||||
|
targetScriptsContentHasher: TargetScriptsContentHasher(contentHasher: contentHasher),
|
||||||
coreDataModelsContentHasher: CoreDataModelsContentHasher(contentHasher: contentHasher),
|
coreDataModelsContentHasher: CoreDataModelsContentHasher(contentHasher: contentHasher),
|
||||||
resourcesContentHasher: ResourcesContentHasher(contentHasher: contentHasher),
|
resourcesContentHasher: ResourcesContentHasher(contentHasher: contentHasher),
|
||||||
headersContentHasher: HeadersContentHasher(contentHasher: contentHasher),
|
headersContentHasher: HeadersContentHasher(contentHasher: contentHasher),
|
||||||
|
@ -42,6 +44,7 @@ public final class TargetContentHasher: TargetContentHashing {
|
||||||
contentHasher: ContentHashing,
|
contentHasher: ContentHashing,
|
||||||
sourceFilesContentHasher: SourceFilesContentHashing,
|
sourceFilesContentHasher: SourceFilesContentHashing,
|
||||||
targetActionsContentHasher: TargetActionsContentHashing,
|
targetActionsContentHasher: TargetActionsContentHashing,
|
||||||
|
targetScriptsContentHasher: TargetScriptsContentHashing,
|
||||||
coreDataModelsContentHasher: CoreDataModelsContentHashing,
|
coreDataModelsContentHasher: CoreDataModelsContentHashing,
|
||||||
resourcesContentHasher: ResourcesContentHashing,
|
resourcesContentHasher: ResourcesContentHashing,
|
||||||
headersContentHasher: HeadersContentHashing,
|
headersContentHasher: HeadersContentHashing,
|
||||||
|
@ -54,6 +57,7 @@ public final class TargetContentHasher: TargetContentHashing {
|
||||||
self.sourceFilesContentHasher = sourceFilesContentHasher
|
self.sourceFilesContentHasher = sourceFilesContentHasher
|
||||||
self.coreDataModelsContentHasher = coreDataModelsContentHasher
|
self.coreDataModelsContentHasher = coreDataModelsContentHasher
|
||||||
self.targetActionsContentHasher = targetActionsContentHasher
|
self.targetActionsContentHasher = targetActionsContentHasher
|
||||||
|
self.targetScriptsContentHasher = targetScriptsContentHasher
|
||||||
self.resourcesContentHasher = resourcesContentHasher
|
self.resourcesContentHasher = resourcesContentHasher
|
||||||
self.headersContentHasher = headersContentHasher
|
self.headersContentHasher = headersContentHasher
|
||||||
self.deploymentTargetContentHasher = deploymentTargetContentHasher
|
self.deploymentTargetContentHasher = deploymentTargetContentHasher
|
||||||
|
@ -64,12 +68,13 @@ public final class TargetContentHasher: TargetContentHashing {
|
||||||
|
|
||||||
// MARK: - TargetContentHashing
|
// MARK: - TargetContentHashing
|
||||||
|
|
||||||
public func contentHash(for targetNode: TargetNode) throws -> String {
|
public func contentHash(for targetNode: TargetNode, cacheOutputType: CacheOutputType) throws -> String {
|
||||||
let target = targetNode.target
|
let target = targetNode.target
|
||||||
let sourcesHash = try sourceFilesContentHasher.hash(sources: target.sources)
|
let sourcesHash = try sourceFilesContentHasher.hash(sources: target.sources)
|
||||||
let resourcesHash = try resourcesContentHasher.hash(resources: target.resources)
|
let resourcesHash = try resourcesContentHasher.hash(resources: target.resources)
|
||||||
let coreDataModelHash = try coreDataModelsContentHasher.hash(coreDataModels: target.coreDataModels)
|
let coreDataModelHash = try coreDataModelsContentHasher.hash(coreDataModels: target.coreDataModels)
|
||||||
let targetActionsHash = try targetActionsContentHasher.hash(targetActions: target.actions)
|
let targetActionsHash = try targetActionsContentHasher.hash(targetActions: target.actions)
|
||||||
|
let targetScriptsHash = try targetScriptsContentHasher.hash(targetScripts: target.scripts)
|
||||||
let dependenciesHash = try dependenciesContentHasher.hash(dependencies: target.dependencies)
|
let dependenciesHash = try dependenciesContentHasher.hash(dependencies: target.dependencies)
|
||||||
let environmentHash = try contentHasher.hash(target.environment)
|
let environmentHash = try contentHasher.hash(target.environment)
|
||||||
var stringsToHash = [target.name,
|
var stringsToHash = [target.name,
|
||||||
|
@ -82,6 +87,7 @@ public final class TargetContentHasher: TargetContentHashing {
|
||||||
resourcesHash,
|
resourcesHash,
|
||||||
coreDataModelHash,
|
coreDataModelHash,
|
||||||
targetActionsHash,
|
targetActionsHash,
|
||||||
|
targetScriptsHash,
|
||||||
environmentHash]
|
environmentHash]
|
||||||
if let headers = target.headers {
|
if let headers = target.headers {
|
||||||
let headersHash = try headersContentHasher.hash(headers: headers)
|
let headersHash = try headersContentHasher.hash(headers: headers)
|
||||||
|
@ -104,6 +110,7 @@ public final class TargetContentHasher: TargetContentHashing {
|
||||||
stringsToHash.append(settingsHash)
|
stringsToHash.append(settingsHash)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
stringsToHash.append(cacheOutputType.description)
|
||||||
return try contentHasher.hash(stringsToHash)
|
return try contentHasher.hash(stringsToHash)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,35 @@
|
||||||
|
import Foundation
|
||||||
|
import TSCBasic
|
||||||
|
import TuistCore
|
||||||
|
|
||||||
|
public protocol TargetScriptsContentHashing {
|
||||||
|
func hash(targetScripts: [TargetScript]) throws -> String
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `TargetScriptsContentHasher`
|
||||||
|
/// is responsible for computing a unique hash that identifies a list of target scripts
|
||||||
|
public final class TargetScriptsContentHasher: TargetScriptsContentHashing {
|
||||||
|
private let contentHasher: ContentHashing
|
||||||
|
|
||||||
|
// MARK: - Init
|
||||||
|
|
||||||
|
public init(contentHasher: ContentHashing) {
|
||||||
|
self.contentHasher = contentHasher
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - TargetScriptsContentHashing
|
||||||
|
|
||||||
|
/// Returns the hash that uniquely identifies an array of target actions
|
||||||
|
/// The hash takes into consideration the content of the script to execute, the content of input/output files, the name of the tool to execute, the order, the arguments and its name
|
||||||
|
public func hash(targetScripts: [TargetScript]) throws -> String {
|
||||||
|
var stringsToHash: [String] = []
|
||||||
|
for targetScript in targetScripts {
|
||||||
|
if targetScript.hashable {
|
||||||
|
stringsToHash.append(targetScript.name)
|
||||||
|
stringsToHash.append(targetScript.script)
|
||||||
|
stringsToHash.append("\(targetScript.showEnvVarsInLog)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return try contentHasher.hash(stringsToHash)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,32 @@
|
||||||
|
import Foundation
|
||||||
|
import RxBlocking
|
||||||
|
import RxSwift
|
||||||
|
import TSCBasic
|
||||||
|
import TuistCore
|
||||||
|
import TuistSupport
|
||||||
|
|
||||||
|
public class CacheBuildPhaseProjectMapper: ProjectMapping {
|
||||||
|
public init() {}
|
||||||
|
|
||||||
|
public func map(project: Project) throws -> (Project, [SideEffectDescriptor]) {
|
||||||
|
let project = project.with(targets: project.targets.map { target in
|
||||||
|
var target = target
|
||||||
|
if target.product.isFramework {
|
||||||
|
target.scripts.append(.init(name: "[Tuist] Create file to locate the built products directory",
|
||||||
|
script: script(target: target),
|
||||||
|
showEnvVarsInLog: true,
|
||||||
|
hashable: false))
|
||||||
|
}
|
||||||
|
return target
|
||||||
|
})
|
||||||
|
return (project, [])
|
||||||
|
}
|
||||||
|
|
||||||
|
fileprivate func script(target: Target) -> String {
|
||||||
|
"""
|
||||||
|
if [ -n "$\(target.targetLocatorBuildPhaseVariable)" ]; then
|
||||||
|
touch $BUILT_PRODUCTS_DIR/.$\(target.targetLocatorBuildPhaseVariable).tuist
|
||||||
|
fi
|
||||||
|
"""
|
||||||
|
}
|
||||||
|
}
|
|
@ -10,13 +10,13 @@ protocol CacheGraphMutating {
|
||||||
/// to the .xcframeworks in the cache, it mutates the graph to link the enry nodes against the .xcframeworks instead.
|
/// to the .xcframeworks in the cache, it mutates the graph to link the enry nodes against the .xcframeworks instead.
|
||||||
/// - Parameters:
|
/// - Parameters:
|
||||||
/// - graph: Dependency graph.
|
/// - graph: Dependency graph.
|
||||||
/// - xcframeworks: Dictionary that maps targets with the paths to their cached .xcframeworks.
|
/// - precompiledFrameworks: Dictionary that maps targets with the paths to their cached `.framework`s or `.xcframework`s.
|
||||||
/// - source: Contains a list of targets that won't be replaced with their pre-compiled version from the cache.
|
/// - source: Contains a list of targets that won't be replaced with their pre-compiled version from the cache.
|
||||||
func map(graph: Graph, xcframeworks: [TargetNode: AbsolutePath], sources: Set<String>) throws -> Graph
|
func map(graph: Graph, precompiledFrameworks: [TargetNode: AbsolutePath], sources: Set<String>) throws -> Graph
|
||||||
}
|
}
|
||||||
|
|
||||||
class CacheGraphMutator: CacheGraphMutating {
|
class CacheGraphMutator: CacheGraphMutating {
|
||||||
struct VisitedXCFramework {
|
struct VisitedPrecompiledFramework {
|
||||||
let path: AbsolutePath?
|
let path: AbsolutePath?
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -25,54 +25,66 @@ class CacheGraphMutator: CacheGraphMutating {
|
||||||
/// Utility to parse an .xcframework from the filesystem and load it into memory.
|
/// Utility to parse an .xcframework from the filesystem and load it into memory.
|
||||||
private let xcframeworkLoader: XCFrameworkNodeLoading
|
private let xcframeworkLoader: XCFrameworkNodeLoading
|
||||||
|
|
||||||
|
/// Utility to parse a .framework from the filesystem and load it into memory.
|
||||||
|
private let frameworkLoader: FrameworkNodeLoading
|
||||||
|
|
||||||
/// Initializes the graph mapper with its attributes.
|
/// Initializes the graph mapper with its attributes.
|
||||||
/// - Parameter xcframeworkLoader: Utility to parse an .xcframework from the filesystem and load it into memory.
|
/// - Parameter xcframeworkLoader: Utility to parse an .xcframework from the filesystem and load it into memory.
|
||||||
init(xcframeworkLoader: XCFrameworkNodeLoading = XCFrameworkNodeLoader()) {
|
init(frameworkLoader: FrameworkNodeLoading = FrameworkNodeLoader(),
|
||||||
|
xcframeworkLoader: XCFrameworkNodeLoading = XCFrameworkNodeLoader())
|
||||||
|
{
|
||||||
|
self.frameworkLoader = frameworkLoader
|
||||||
self.xcframeworkLoader = xcframeworkLoader
|
self.xcframeworkLoader = xcframeworkLoader
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - CacheGraphMapping
|
// MARK: - CacheGraphMapping
|
||||||
|
|
||||||
public func map(graph: Graph, xcframeworks: [TargetNode: AbsolutePath], sources: Set<String>) throws -> Graph {
|
public func map(graph: Graph, precompiledFrameworks: [TargetNode: AbsolutePath], sources: Set<String>) throws -> Graph {
|
||||||
var visitedXCFrameworkPaths: [TargetNode: VisitedXCFramework?] = [:]
|
var visitedPrecompiledFrameworkPaths: [TargetNode: VisitedPrecompiledFramework?] = [:]
|
||||||
var loadedXCFrameworks: [AbsolutePath: XCFrameworkNode] = [:]
|
var loadedPrecompiledNodes: [AbsolutePath: PrecompiledNode] = [:]
|
||||||
let userSpecifiedSourceTargets = graph.targets.flatMap { $0.value }.filter { sources.contains($0.target.name) }
|
let userSpecifiedSourceTargets = graph.targets.flatMap { $0.value }.filter { sources.contains($0.target.name) }
|
||||||
let userSpecifiedSourceTestTargets = userSpecifiedSourceTargets.flatMap { graph.testTargetsDependingOn(path: $0.path, name: $0.name) }
|
let userSpecifiedSourceTestTargets = userSpecifiedSourceTargets.flatMap { graph.testTargetsDependingOn(path: $0.path, name: $0.name) }
|
||||||
var sourceTargets: Set<TargetNode> = Set(userSpecifiedSourceTargets)
|
var sourceTargets: Set<TargetNode> = Set(userSpecifiedSourceTargets)
|
||||||
|
|
||||||
try (userSpecifiedSourceTargets + userSpecifiedSourceTestTargets)
|
try (userSpecifiedSourceTargets + userSpecifiedSourceTestTargets)
|
||||||
.forEach { try visit(targetNode: $0,
|
.forEach { try visit(targetNode: $0,
|
||||||
xcframeworks: xcframeworks,
|
precompiledFrameworks: precompiledFrameworks,
|
||||||
sources: sources,
|
sources: sources,
|
||||||
sourceTargets: &sourceTargets,
|
sourceTargets: &sourceTargets,
|
||||||
visitedXCFrameworkPaths: &visitedXCFrameworkPaths,
|
visitedPrecompiledFrameworkPaths: &visitedPrecompiledFrameworkPaths,
|
||||||
loadedXCFrameworks: &loadedXCFrameworks) }
|
loadedPrecompiledNodes: &loadedPrecompiledNodes) }
|
||||||
|
|
||||||
return treeShake(graph: graph, sourceTargets: sourceTargets)
|
// We mark them to be pruned during the tree-shaking
|
||||||
|
graph.targets.flatMap(\.value).forEach {
|
||||||
|
if !sourceTargets.contains($0) { $0.prune = true }
|
||||||
|
}
|
||||||
|
|
||||||
|
return graph
|
||||||
}
|
}
|
||||||
|
|
||||||
fileprivate func visit(targetNode: TargetNode,
|
fileprivate func visit(targetNode: TargetNode,
|
||||||
xcframeworks: [TargetNode: AbsolutePath],
|
precompiledFrameworks: [TargetNode: AbsolutePath],
|
||||||
sources: Set<String>,
|
sources: Set<String>,
|
||||||
sourceTargets: inout Set<TargetNode>,
|
sourceTargets: inout Set<TargetNode>,
|
||||||
visitedXCFrameworkPaths: inout [TargetNode: VisitedXCFramework?],
|
visitedPrecompiledFrameworkPaths: inout [TargetNode: VisitedPrecompiledFramework?],
|
||||||
loadedXCFrameworks: inout [AbsolutePath: XCFrameworkNode]) throws
|
loadedPrecompiledNodes: inout [AbsolutePath: PrecompiledNode]) throws
|
||||||
{
|
{
|
||||||
sourceTargets.formUnion([targetNode])
|
sourceTargets.formUnion([targetNode])
|
||||||
targetNode.dependencies = try mapDependencies(targetNode.dependencies,
|
targetNode.dependencies = try mapDependencies(targetNode.dependencies,
|
||||||
xcframeworks: xcframeworks,
|
precompiledFrameworks: precompiledFrameworks,
|
||||||
sources: sources,
|
sources: sources,
|
||||||
sourceTargets: &sourceTargets,
|
sourceTargets: &sourceTargets,
|
||||||
visitedXCFrameworkPaths: &visitedXCFrameworkPaths,
|
visitedPrecompiledFrameworkPaths: &visitedPrecompiledFrameworkPaths,
|
||||||
loadedXCFrameworks: &loadedXCFrameworks)
|
loadedPrecompiledFrameworks: &loadedPrecompiledNodes)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// swiftlint:disable line_length
|
||||||
fileprivate func mapDependencies(_ dependencies: [GraphNode],
|
fileprivate func mapDependencies(_ dependencies: [GraphNode],
|
||||||
xcframeworks: [TargetNode: AbsolutePath],
|
precompiledFrameworks: [TargetNode: AbsolutePath],
|
||||||
sources: Set<String>,
|
sources: Set<String>,
|
||||||
sourceTargets: inout Set<TargetNode>,
|
sourceTargets: inout Set<TargetNode>,
|
||||||
visitedXCFrameworkPaths: inout [TargetNode: VisitedXCFramework?],
|
visitedPrecompiledFrameworkPaths: inout [TargetNode: VisitedPrecompiledFramework?],
|
||||||
loadedXCFrameworks: inout [AbsolutePath: XCFrameworkNode]) throws -> [GraphNode]
|
loadedPrecompiledFrameworks: inout [AbsolutePath: PrecompiledNode]) throws -> [GraphNode]
|
||||||
{
|
{
|
||||||
var newDependencies: [GraphNode] = []
|
var newDependencies: [GraphNode] = []
|
||||||
try dependencies.forEach { dependency in
|
try dependencies.forEach { dependency in
|
||||||
|
@ -82,114 +94,80 @@ class CacheGraphMutator: CacheGraphMutating {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the target cannot be replaced with its associated .xcframework we return
|
// If the target cannot be replaced with its associated .(xc)framework we return
|
||||||
guard !sources.contains(targetDependency.target.name), let xcframeworkPath = xcframeworkPath(target: targetDependency,
|
guard !sources.contains(targetDependency.target.name), let precompiledFrameworkPath = precompiledFrameworkPath(target: targetDependency,
|
||||||
xcframeworks: xcframeworks,
|
precompiledFrameworks: precompiledFrameworks,
|
||||||
visitedXCFrameworkPaths: &visitedXCFrameworkPaths)
|
visitedPrecompiledFrameworkPaths: &visitedPrecompiledFrameworkPaths)
|
||||||
else {
|
else {
|
||||||
sourceTargets.formUnion([targetDependency])
|
sourceTargets.formUnion([targetDependency])
|
||||||
targetDependency.dependencies = try mapDependencies(targetDependency.dependencies,
|
targetDependency.dependencies = try mapDependencies(targetDependency.dependencies,
|
||||||
xcframeworks: xcframeworks,
|
precompiledFrameworks: precompiledFrameworks,
|
||||||
sources: sources,
|
sources: sources,
|
||||||
sourceTargets: &sourceTargets,
|
sourceTargets: &sourceTargets,
|
||||||
visitedXCFrameworkPaths: &visitedXCFrameworkPaths,
|
visitedPrecompiledFrameworkPaths: &visitedPrecompiledFrameworkPaths,
|
||||||
loadedXCFrameworks: &loadedXCFrameworks)
|
loadedPrecompiledFrameworks: &loadedPrecompiledFrameworks)
|
||||||
newDependencies.append(targetDependency)
|
newDependencies.append(targetDependency)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// We load the xcframework
|
// We load the .framework (or fallback on .xcframework)
|
||||||
let xcframework = try self.loadXCFramework(path: xcframeworkPath, loadedXCFrameworks: &loadedXCFrameworks)
|
let precompiledFramework: PrecompiledNode = try loadPrecompiledFramework(path: precompiledFrameworkPath, loadedPrecompiledFrameworks: &loadedPrecompiledFrameworks)
|
||||||
|
|
||||||
try mapDependencies(targetDependency.dependencies,
|
try mapDependencies(targetDependency.dependencies,
|
||||||
xcframeworks: xcframeworks,
|
precompiledFrameworks: precompiledFrameworks,
|
||||||
sources: sources,
|
sources: sources,
|
||||||
sourceTargets: &sourceTargets,
|
sourceTargets: &sourceTargets,
|
||||||
visitedXCFrameworkPaths: &visitedXCFrameworkPaths,
|
visitedPrecompiledFrameworkPaths: &visitedPrecompiledFrameworkPaths,
|
||||||
loadedXCFrameworks: &loadedXCFrameworks).forEach { dependency in
|
loadedPrecompiledFrameworks: &loadedPrecompiledFrameworks).forEach { dependency in
|
||||||
if let frameworkDependency = dependency as? FrameworkNode {
|
if let frameworkDependency = dependency as? FrameworkNode {
|
||||||
xcframework.add(dependency: XCFrameworkNode.Dependency.framework(frameworkDependency))
|
precompiledFramework.add(dependency: PrecompiledNode.Dependency.framework(frameworkDependency))
|
||||||
} else if let xcframeworkDependency = dependency as? XCFrameworkNode {
|
} else if let xcframeworkDependency = dependency as? XCFrameworkNode {
|
||||||
xcframework.add(dependency: XCFrameworkNode.Dependency.xcframework(xcframeworkDependency))
|
precompiledFramework.add(dependency: PrecompiledNode.Dependency.xcframework(xcframeworkDependency))
|
||||||
} else {
|
} else {
|
||||||
// Static dependencies fall into this case.
|
// Static dependencies fall into this case.
|
||||||
// Those are now part of the precompiled xcframework and therefore we don't have to link against them.
|
// Those are now part of the precompiled (xc)framework and therefore we don't have to link against them.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
newDependencies.append(xcframework)
|
newDependencies.append(precompiledFramework)
|
||||||
}
|
}
|
||||||
return newDependencies
|
return newDependencies
|
||||||
}
|
}
|
||||||
|
|
||||||
func treeShake(graph: Graph, sourceTargets: Set<TargetNode>) -> Graph {
|
fileprivate func loadPrecompiledFramework(path: AbsolutePath, loadedPrecompiledFrameworks: inout [AbsolutePath: PrecompiledNode]) throws -> PrecompiledNode {
|
||||||
let targetReferences = Set(sourceTargets.map { TargetReference(projectPath: $0.path, name: $0.name) })
|
if let cachedFramework = loadedPrecompiledFrameworks[path] {
|
||||||
|
return cachedFramework
|
||||||
let projects = graph.projects.compactMap { (project) -> Project? in
|
} else if let framework = try? frameworkLoader.load(path: path) {
|
||||||
let targets: [Target] = project.targets.compactMap { (target) -> Target? in
|
loadedPrecompiledFrameworks[path] = framework
|
||||||
guard let targetNode = graph.target(path: project.path, name: target.name) else { return nil }
|
return framework
|
||||||
guard sourceTargets.contains(targetNode) else { return nil }
|
|
||||||
return target
|
|
||||||
}
|
|
||||||
if targets.isEmpty {
|
|
||||||
return nil
|
|
||||||
} else {
|
} else {
|
||||||
let schemes: [Scheme] = project.schemes.compactMap { scheme -> Scheme? in
|
|
||||||
let buildActionTargets = scheme.buildAction?.targets.filter { targetReferences.contains($0) } ?? []
|
|
||||||
|
|
||||||
// The scheme contains no buildable targets so we don't include it.
|
|
||||||
if buildActionTargets.isEmpty { return nil }
|
|
||||||
|
|
||||||
let testActionTargets = scheme.testAction?.targets.filter { targetReferences.contains($0.target) } ?? []
|
|
||||||
var scheme = scheme
|
|
||||||
var buildAction = scheme.buildAction
|
|
||||||
var testAction = scheme.testAction
|
|
||||||
buildAction?.targets = buildActionTargets
|
|
||||||
testAction?.targets = testActionTargets
|
|
||||||
scheme.buildAction = buildAction
|
|
||||||
scheme.testAction = testAction
|
|
||||||
|
|
||||||
return scheme
|
|
||||||
}
|
|
||||||
return project.with(targets: targets).with(schemes: schemes)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return graph
|
|
||||||
.with(projects: projects)
|
|
||||||
.with(targets: sourceTargets.reduce(into: [AbsolutePath: [TargetNode]]()) { acc, target in
|
|
||||||
var targets = acc[target.path, default: []]
|
|
||||||
targets.append(target)
|
|
||||||
acc[target.path] = targets
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fileprivate func loadXCFramework(path: AbsolutePath, loadedXCFrameworks: inout [AbsolutePath: XCFrameworkNode]) throws -> XCFrameworkNode {
|
|
||||||
if let cachedXCFramework = loadedXCFrameworks[path] { return cachedXCFramework }
|
|
||||||
let xcframework = try xcframeworkLoader.load(path: path)
|
let xcframework = try xcframeworkLoader.load(path: path)
|
||||||
loadedXCFrameworks[path] = xcframework
|
loadedPrecompiledFrameworks[path] = xcframework
|
||||||
return xcframework
|
return xcframework
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fileprivate func xcframeworkPath(target: TargetNode,
|
fileprivate func precompiledFrameworkPath(target: TargetNode,
|
||||||
xcframeworks: [TargetNode: AbsolutePath],
|
precompiledFrameworks: [TargetNode: AbsolutePath],
|
||||||
visitedXCFrameworkPaths: inout [TargetNode: VisitedXCFramework?]) -> AbsolutePath?
|
visitedPrecompiledFrameworkPaths: inout [TargetNode: VisitedPrecompiledFramework?]) -> AbsolutePath?
|
||||||
{
|
{
|
||||||
// Already visited
|
// Already visited
|
||||||
if let visited = visitedXCFrameworkPaths[target] { return visited?.path }
|
if let visited = visitedPrecompiledFrameworkPaths[target] { return visited?.path }
|
||||||
|
|
||||||
// The target doesn't have a cached xcframework
|
// The target doesn't have a cached .(xc)framework
|
||||||
if xcframeworks[target] == nil {
|
if precompiledFrameworks[target] == nil {
|
||||||
visitedXCFrameworkPaths[target] = VisitedXCFramework(path: nil)
|
visitedPrecompiledFrameworkPaths[target] = VisitedPrecompiledFramework(path: nil)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
// The target can be replaced
|
// The target can be replaced
|
||||||
else if let path = xcframeworks[target],
|
else if let path = precompiledFrameworks[target],
|
||||||
target.targetDependencies.allSatisfy({ xcframeworkPath(target: $0, xcframeworks: xcframeworks,
|
target.targetDependencies.allSatisfy({ precompiledFrameworkPath(target: $0,
|
||||||
visitedXCFrameworkPaths: &visitedXCFrameworkPaths) != nil })
|
precompiledFrameworks: precompiledFrameworks,
|
||||||
|
visitedPrecompiledFrameworkPaths: &visitedPrecompiledFrameworkPaths) != nil })
|
||||||
{
|
{
|
||||||
visitedXCFrameworkPaths[target] = VisitedXCFramework(path: path)
|
visitedPrecompiledFrameworkPaths[target] = VisitedPrecompiledFramework(path: path)
|
||||||
return path
|
return path
|
||||||
} else {
|
} else {
|
||||||
visitedXCFrameworkPaths[target] = VisitedXCFramework(path: nil)
|
visitedPrecompiledFrameworkPaths[target] = VisitedPrecompiledFramework(path: nil)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,7 +17,7 @@ public class CacheMapper: GraphMapping {
|
||||||
/// Cache graph mapper.
|
/// Cache graph mapper.
|
||||||
private let cacheGraphMutator: CacheGraphMutating
|
private let cacheGraphMutator: CacheGraphMutating
|
||||||
|
|
||||||
/// Configuration object
|
/// Configuration object.
|
||||||
private let config: Config
|
private let config: Config
|
||||||
|
|
||||||
/// List of targets that will be generated as sources instead of pre-compiled targets from the cache.
|
/// List of targets that will be generated as sources instead of pre-compiled targets from the cache.
|
||||||
|
@ -26,22 +26,28 @@ public class CacheMapper: GraphMapping {
|
||||||
/// Dispatch queue.
|
/// Dispatch queue.
|
||||||
private let queue: DispatchQueue
|
private let queue: DispatchQueue
|
||||||
|
|
||||||
|
/// The type of artifact that the hasher is configured with.
|
||||||
|
private let cacheOutputType: CacheOutputType
|
||||||
|
|
||||||
// MARK: - Init
|
// MARK: - Init
|
||||||
|
|
||||||
public convenience init(config: Config,
|
public convenience init(config: Config,
|
||||||
cacheStorageProvider: CacheStorageProviding,
|
cacheStorageProvider: CacheStorageProviding,
|
||||||
sources: Set<String>)
|
sources: Set<String>,
|
||||||
|
cacheOutputType: CacheOutputType)
|
||||||
{
|
{
|
||||||
self.init(config: config,
|
self.init(config: config,
|
||||||
cache: Cache(storageProvider: cacheStorageProvider),
|
cache: Cache(storageProvider: cacheStorageProvider),
|
||||||
graphContentHasher: GraphContentHasher(),
|
graphContentHasher: GraphContentHasher(),
|
||||||
sources: sources)
|
sources: sources,
|
||||||
|
cacheOutputType: cacheOutputType)
|
||||||
}
|
}
|
||||||
|
|
||||||
init(config: Config,
|
init(config: Config,
|
||||||
cache: CacheStoring,
|
cache: CacheStoring,
|
||||||
graphContentHasher: GraphContentHashing,
|
graphContentHasher: GraphContentHashing,
|
||||||
sources: Set<String>,
|
sources: Set<String>,
|
||||||
|
cacheOutputType: CacheOutputType,
|
||||||
cacheGraphMutator: CacheGraphMutating = CacheGraphMutator(),
|
cacheGraphMutator: CacheGraphMutating = CacheGraphMutator(),
|
||||||
queue: DispatchQueue = CacheMapper.dispatchQueue())
|
queue: DispatchQueue = CacheMapper.dispatchQueue())
|
||||||
{
|
{
|
||||||
|
@ -51,6 +57,7 @@ public class CacheMapper: GraphMapping {
|
||||||
self.queue = queue
|
self.queue = queue
|
||||||
self.cacheGraphMutator = cacheGraphMutator
|
self.cacheGraphMutator = cacheGraphMutator
|
||||||
self.sources = sources
|
self.sources = sources
|
||||||
|
self.cacheOutputType = cacheOutputType
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - GraphMapping
|
// MARK: - GraphMapping
|
||||||
|
@ -70,7 +77,8 @@ public class CacheMapper: GraphMapping {
|
||||||
fileprivate func hashes(graph: Graph) -> Single<[TargetNode: String]> {
|
fileprivate func hashes(graph: Graph) -> Single<[TargetNode: String]> {
|
||||||
Single.create { (observer) -> Disposable in
|
Single.create { (observer) -> Disposable in
|
||||||
do {
|
do {
|
||||||
let hashes = try self.graphContentHasher.contentHashes(for: graph)
|
let hashes = try self.graphContentHasher.contentHashes(for: graph,
|
||||||
|
cacheOutputType: self.cacheOutputType)
|
||||||
observer(.success(hashes))
|
observer(.success(hashes))
|
||||||
} catch {
|
} catch {
|
||||||
observer(.error(error))
|
observer(.error(error))
|
||||||
|
@ -83,7 +91,7 @@ public class CacheMapper: GraphMapping {
|
||||||
fileprivate func map(graph: Graph, hashes: [TargetNode: String], sources: Set<String>) -> Single<Graph> {
|
fileprivate func map(graph: Graph, hashes: [TargetNode: String], sources: Set<String>) -> Single<Graph> {
|
||||||
fetch(hashes: hashes).map { xcframeworkPaths in
|
fetch(hashes: hashes).map { xcframeworkPaths in
|
||||||
try self.cacheGraphMutator.map(graph: graph,
|
try self.cacheGraphMutator.map(graph: graph,
|
||||||
xcframeworks: xcframeworkPaths,
|
precompiledFrameworks: xcframeworkPaths,
|
||||||
sources: sources)
|
sources: sources)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,72 @@
|
||||||
|
import Foundation
|
||||||
|
import TSCBasic
|
||||||
|
import TuistCore
|
||||||
|
|
||||||
|
public final class CacheTreeShakingGraphMapper: GraphMapping {
|
||||||
|
public init() {}
|
||||||
|
|
||||||
|
public func map(graph: Graph) throws -> (Graph, [SideEffectDescriptor]) {
|
||||||
|
let sourceTargets: Set<TargetReference> = graph.targets.reduce(into: Set<TargetReference>()) { acc, next in
|
||||||
|
acc.formUnion(next.value.filter { !$0.prune }.map { TargetReference(projectPath: $0.path, name: $0.name) })
|
||||||
|
}
|
||||||
|
// If the number of source targets matches the number of targets in the graph there's nothing to be pruned.
|
||||||
|
if sourceTargets.count == graph.targets.flatMap(\.value).count { return (graph, []) }
|
||||||
|
|
||||||
|
let projects = graph.projects.compactMap { (project) -> Project? in
|
||||||
|
let targets = self.treeShake(targets: project.targets,
|
||||||
|
path: project.path,
|
||||||
|
graph: graph,
|
||||||
|
sourceTargets: sourceTargets)
|
||||||
|
|
||||||
|
// If the project has no targets we remove the project.
|
||||||
|
if targets.isEmpty {
|
||||||
|
return nil
|
||||||
|
} else {
|
||||||
|
let schemes = self.treeShake(schemes: project.schemes,
|
||||||
|
sourceTargets: sourceTargets)
|
||||||
|
return project.with(targets: targets).with(schemes: schemes)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let graph = graph
|
||||||
|
.with(projects: projects)
|
||||||
|
.with(targets: sourceTargets.reduce(into: [AbsolutePath: [TargetNode]]()) { acc, targetReference in
|
||||||
|
var targets = acc[targetReference.projectPath, default: []]
|
||||||
|
if let target = graph.target(path: targetReference.projectPath, name: targetReference.name) {
|
||||||
|
targets.append(target)
|
||||||
|
}
|
||||||
|
acc[targetReference.projectPath] = targets
|
||||||
|
})
|
||||||
|
|
||||||
|
return (graph, [])
|
||||||
|
}
|
||||||
|
|
||||||
|
fileprivate func treeShake(targets: [Target], path: AbsolutePath, graph: Graph, sourceTargets: Set<TargetReference>) -> [Target] {
|
||||||
|
targets.compactMap { (target) -> Target? in
|
||||||
|
guard let targetNode = graph.target(path: path, name: target.name) else { return nil }
|
||||||
|
let targetReference = TargetReference(projectPath: targetNode.path, name: targetNode.name)
|
||||||
|
guard sourceTargets.contains(targetReference) else { return nil }
|
||||||
|
return target
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fileprivate func treeShake(schemes: [Scheme], sourceTargets: Set<TargetReference>) -> [Scheme] {
|
||||||
|
schemes.compactMap { scheme -> Scheme? in
|
||||||
|
let buildActionTargets = scheme.buildAction?.targets.filter { sourceTargets.contains($0) } ?? []
|
||||||
|
|
||||||
|
// The scheme contains no buildable targets so we don't include it.
|
||||||
|
if buildActionTargets.isEmpty { return nil }
|
||||||
|
|
||||||
|
let testActionTargets = scheme.testAction?.targets.filter { sourceTargets.contains($0.target) } ?? []
|
||||||
|
var scheme = scheme
|
||||||
|
var buildAction = scheme.buildAction
|
||||||
|
var testAction = scheme.testAction
|
||||||
|
buildAction?.targets = buildActionTargets
|
||||||
|
testAction?.targets = testActionTargets
|
||||||
|
scheme.buildAction = buildAction
|
||||||
|
scheme.testAction = testAction
|
||||||
|
|
||||||
|
return scheme
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,23 @@
|
||||||
|
import TSCBasic
|
||||||
|
import TuistCore
|
||||||
|
|
||||||
|
public protocol CacheArtifactBuilding {
|
||||||
|
/// Returns the type of artifact that the concrete builder processes.
|
||||||
|
var cacheOutputType: CacheOutputType { get }
|
||||||
|
|
||||||
|
/// Builds a given target and outputs the cacheable artifact into the given directory.
|
||||||
|
///
|
||||||
|
/// - Parameters:
|
||||||
|
/// - workspacePath: Path to the generated .xcworkspace that contains the given target.
|
||||||
|
/// - target: Target whose artifact will be generated.
|
||||||
|
/// - into: The directory into which the output artifacts will be copied.
|
||||||
|
func build(workspacePath: AbsolutePath, target: Target, into outputDirectory: AbsolutePath) throws
|
||||||
|
|
||||||
|
/// Builds a given target and outputs the cacheable artifact into the given directory.
|
||||||
|
///
|
||||||
|
/// - Parameters:
|
||||||
|
/// - projectPath: Path to the generated .xcodeproj that contains the given target.
|
||||||
|
/// - target: Target whose .(xc)framework will be generated.
|
||||||
|
/// - into: The directory into which the output artifacts will be copied.
|
||||||
|
func build(projectPath: AbsolutePath, target: Target, into outputDirectory: AbsolutePath) throws
|
||||||
|
}
|
|
@ -0,0 +1,24 @@
|
||||||
|
import TuistSupport
|
||||||
|
|
||||||
|
enum CacheBinaryBuilderError: FatalError {
|
||||||
|
case nonFrameworkTargetForXCFramework(String)
|
||||||
|
case nonFrameworkTargetForFramework(String)
|
||||||
|
|
||||||
|
/// Error type.
|
||||||
|
var type: ErrorType {
|
||||||
|
switch self {
|
||||||
|
case .nonFrameworkTargetForXCFramework: return .abort
|
||||||
|
case .nonFrameworkTargetForFramework: return .abort
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Error description.
|
||||||
|
var description: String {
|
||||||
|
switch self {
|
||||||
|
case let .nonFrameworkTargetForXCFramework(name):
|
||||||
|
return "Can't generate an .xcframework from the target '\(name)' because it's not a framework target"
|
||||||
|
case let .nonFrameworkTargetForFramework(name):
|
||||||
|
return "Can't generate a .framework from the target '\(name)' because it's not a framework target"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,209 @@
|
||||||
|
import Foundation
|
||||||
|
import RxBlocking
|
||||||
|
import RxSwift
|
||||||
|
import TSCBasic
|
||||||
|
import TuistCore
|
||||||
|
import TuistSupport
|
||||||
|
|
||||||
|
public enum CacheFrameworkBuilderError: FatalError {
|
||||||
|
case builtProductsDirectoryNotFound(targetName: String)
|
||||||
|
case frameworkNotFound(name: String, derivedDataPath: AbsolutePath)
|
||||||
|
case deviceNotFound(platform: String)
|
||||||
|
|
||||||
|
public var description: String {
|
||||||
|
switch self {
|
||||||
|
case let .builtProductsDirectoryNotFound(targetName):
|
||||||
|
return "Couldn't find the built products directory for target '\(targetName)'."
|
||||||
|
case let .frameworkNotFound(name, derivedDataPath):
|
||||||
|
return "Couldn't find framework '\(name)' in the derived data directory: \(derivedDataPath.pathString)"
|
||||||
|
case let .deviceNotFound(platform):
|
||||||
|
return "Couldn't find an available device for platform: '\(platform)'"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public var type: ErrorType {
|
||||||
|
switch self {
|
||||||
|
case .builtProductsDirectoryNotFound: return .bug
|
||||||
|
case .frameworkNotFound: return .bug
|
||||||
|
case .deviceNotFound: return .bug
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public final class CacheFrameworkBuilder: CacheArtifactBuilding {
|
||||||
|
// MARK: - Attributes
|
||||||
|
|
||||||
|
/// Xcode build controller instance to run xcodebuild commands.
|
||||||
|
private let xcodeBuildController: XcodeBuildControlling
|
||||||
|
|
||||||
|
/// Simulator controller.
|
||||||
|
private let simulatorController: SimulatorControlling
|
||||||
|
|
||||||
|
/// Developer's environment.
|
||||||
|
private let developerEnvironment: DeveloperEnvironmenting
|
||||||
|
|
||||||
|
// MARK: - Init
|
||||||
|
|
||||||
|
/// Initialzies the builder.
|
||||||
|
/// - Parameters:
|
||||||
|
/// - xcodeBuildController: Xcode build controller.
|
||||||
|
/// - simulatorController: Simulator controller.
|
||||||
|
/// - developerEnvironment: Developer environment.
|
||||||
|
public init(xcodeBuildController: XcodeBuildControlling,
|
||||||
|
simulatorController: SimulatorControlling = SimulatorController(),
|
||||||
|
developerEnvironment: DeveloperEnvironmenting = DeveloperEnvironment.shared)
|
||||||
|
{
|
||||||
|
self.xcodeBuildController = xcodeBuildController
|
||||||
|
self.simulatorController = simulatorController
|
||||||
|
self.developerEnvironment = developerEnvironment
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - ArtifactBuilding
|
||||||
|
|
||||||
|
/// Returns the type of artifact that the concrete builder processes
|
||||||
|
public var cacheOutputType: CacheOutputType = .framework
|
||||||
|
|
||||||
|
public func build(workspacePath: AbsolutePath,
|
||||||
|
target: Target,
|
||||||
|
into outputDirectory: AbsolutePath) throws
|
||||||
|
{
|
||||||
|
try build(.workspace(workspacePath),
|
||||||
|
target: target,
|
||||||
|
into: outputDirectory)
|
||||||
|
}
|
||||||
|
|
||||||
|
public func build(projectPath: AbsolutePath,
|
||||||
|
target: Target,
|
||||||
|
into outputDirectory: AbsolutePath) throws
|
||||||
|
{
|
||||||
|
try build(.project(projectPath),
|
||||||
|
target: target,
|
||||||
|
into: outputDirectory)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Fileprivate
|
||||||
|
|
||||||
|
fileprivate func build(_ projectTarget: XcodeBuildTarget,
|
||||||
|
target: Target,
|
||||||
|
into outputDirectory: AbsolutePath) throws
|
||||||
|
{
|
||||||
|
guard target.product.isFramework else {
|
||||||
|
throw CacheBinaryBuilderError.nonFrameworkTargetForFramework(target.name)
|
||||||
|
}
|
||||||
|
let scheme = target.name.spm_shellEscaped()
|
||||||
|
|
||||||
|
// Create temporary directories
|
||||||
|
let builtProductsDirFingerprint = String.random()
|
||||||
|
logger.notice("Building .framework for \(target.name)...", metadata: .section)
|
||||||
|
|
||||||
|
let sdk = self.sdk(target: target)
|
||||||
|
let configuration = "Debug" // TODO: Is it available?
|
||||||
|
|
||||||
|
let arguments = try self.arguments(target: target,
|
||||||
|
sdk: sdk,
|
||||||
|
configuration: configuration,
|
||||||
|
builtProductsDirFingerprint: builtProductsDirFingerprint)
|
||||||
|
try xcodebuild(
|
||||||
|
projectTarget: projectTarget,
|
||||||
|
scheme: scheme,
|
||||||
|
target: target,
|
||||||
|
arguments: arguments
|
||||||
|
)
|
||||||
|
|
||||||
|
try exportFrameworksAndDSYMs(into: outputDirectory,
|
||||||
|
target: target,
|
||||||
|
builtProductsDirFingerprint: builtProductsDirFingerprint)
|
||||||
|
}
|
||||||
|
|
||||||
|
fileprivate func arguments(target: Target,
|
||||||
|
sdk: String,
|
||||||
|
configuration: String,
|
||||||
|
builtProductsDirFingerprint: String) throws -> [XcodeBuildArgument]
|
||||||
|
{
|
||||||
|
try destination(target: target)
|
||||||
|
.map { (destination: String) -> [XcodeBuildArgument] in
|
||||||
|
[
|
||||||
|
.sdk(sdk),
|
||||||
|
.configuration(configuration),
|
||||||
|
.buildSetting("DEBUG_INFORMATION_FORMAT", "dwarf-with-dsym"),
|
||||||
|
.buildSetting("GCC_GENERATE_DEBUGGING_SYMBOLS", "YES"),
|
||||||
|
.buildSetting(target.targetLocatorBuildPhaseVariable, builtProductsDirFingerprint),
|
||||||
|
.destination(destination),
|
||||||
|
]
|
||||||
|
}
|
||||||
|
.toBlocking()
|
||||||
|
.single()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// https://www.mokacoding.com/blog/xcodebuild-destination-options/
|
||||||
|
/// https://www.mokacoding.com/blog/how-to-always-run-latest-simulator-cli/
|
||||||
|
fileprivate func destination(target: Target) -> Single<String> {
|
||||||
|
var platform: Platform!
|
||||||
|
switch target.platform {
|
||||||
|
case .iOS: platform = .iOS
|
||||||
|
case .watchOS: platform = .watchOS
|
||||||
|
case .tvOS: platform = .tvOS
|
||||||
|
case .macOS: return .just("platform=OS X,arch=x86_64")
|
||||||
|
}
|
||||||
|
|
||||||
|
return simulatorController.devicesAndRuntimes()
|
||||||
|
.map { (simulatorsAndRuntimes) -> [SimulatorDevice] in
|
||||||
|
simulatorsAndRuntimes
|
||||||
|
.filter { $0.runtime.isAvailable && $0.runtime.name.contains(platform.caseValue) }
|
||||||
|
.map { $0.device }
|
||||||
|
}
|
||||||
|
.flatMap { (devices) -> Single<String> in
|
||||||
|
if let device = devices.first {
|
||||||
|
let destination = "platform=\(platform.caseValue) Simulator,name=\(device.name),OS=latest"
|
||||||
|
return .just(destination)
|
||||||
|
} else {
|
||||||
|
return .error(CacheFrameworkBuilderError.deviceNotFound(platform: target.platform.caseValue))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fileprivate func sdk(target: Target) -> String {
|
||||||
|
if target.platform == .macOS {
|
||||||
|
return target.platform.xcodeDeviceSDK
|
||||||
|
} else {
|
||||||
|
return target.platform.xcodeSimulatorSDK!
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fileprivate func xcodebuild(projectTarget: XcodeBuildTarget,
|
||||||
|
scheme: String,
|
||||||
|
target: Target,
|
||||||
|
arguments: [XcodeBuildArgument]) throws
|
||||||
|
{
|
||||||
|
_ = try xcodeBuildController.build(projectTarget,
|
||||||
|
scheme: scheme,
|
||||||
|
clean: false,
|
||||||
|
arguments: arguments)
|
||||||
|
.printFormattedOutput()
|
||||||
|
.do(onSubscribed: {
|
||||||
|
logger.notice("Building \(target.name) as .framework...", metadata: .subsection)
|
||||||
|
})
|
||||||
|
.ignoreElements()
|
||||||
|
.toBlocking()
|
||||||
|
.last()
|
||||||
|
}
|
||||||
|
|
||||||
|
fileprivate func exportFrameworksAndDSYMs(into outputDirectory: AbsolutePath,
|
||||||
|
target: Target,
|
||||||
|
builtProductsDirFingerprint: String) throws
|
||||||
|
{
|
||||||
|
let globPattern = "**/.\(builtProductsDirFingerprint).tuist"
|
||||||
|
let derivedDataPath = developerEnvironment.derivedDataDirectory
|
||||||
|
guard let directory = FileHandler.shared.glob(derivedDataPath, glob: globPattern).first?.parentDirectory else {
|
||||||
|
throw CacheFrameworkBuilderError.builtProductsDirectoryNotFound(targetName: target.name)
|
||||||
|
}
|
||||||
|
guard let framework = FileHandler.shared.glob(directory, glob: target.productNameWithExtension).first else {
|
||||||
|
throw CacheFrameworkBuilderError.frameworkNotFound(name: target.productNameWithExtension, derivedDataPath: derivedDataPath)
|
||||||
|
}
|
||||||
|
let dsyms = FileHandler.shared.glob(directory, glob: "\(target.productNameWithExtension).dSYM")
|
||||||
|
try FileHandler.shared.copy(from: framework, to: outputDirectory.appending(component: framework.basename))
|
||||||
|
try dsyms.forEach { dsym in
|
||||||
|
try FileHandler.shared.copy(from: dsym, to: outputDirectory.appending(component: dsym.basename))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,159 @@
|
||||||
|
import Foundation
|
||||||
|
import RxBlocking
|
||||||
|
import RxSwift
|
||||||
|
import TSCBasic
|
||||||
|
import TuistCore
|
||||||
|
import TuistSupport
|
||||||
|
|
||||||
|
public final class CacheXCFrameworkBuilder: CacheArtifactBuilding {
|
||||||
|
// MARK: - Attributes
|
||||||
|
|
||||||
|
/// Xcode build controller instance to run xcodebuild commands.
|
||||||
|
private let xcodeBuildController: XcodeBuildControlling
|
||||||
|
|
||||||
|
// MARK: - Init
|
||||||
|
|
||||||
|
/// Initializes the builder.
|
||||||
|
/// - Parameter xcodeBuildController: Xcode build controller instance to run xcodebuild commands.
|
||||||
|
public init(xcodeBuildController: XcodeBuildControlling) {
|
||||||
|
self.xcodeBuildController = xcodeBuildController
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - ArtifactBuilding
|
||||||
|
|
||||||
|
/// Returns the type of artifact that the concrete builder processes
|
||||||
|
public var cacheOutputType: CacheOutputType = .xcframework
|
||||||
|
|
||||||
|
public func build(workspacePath: AbsolutePath,
|
||||||
|
target: Target,
|
||||||
|
into outputDirectory: AbsolutePath) throws
|
||||||
|
{
|
||||||
|
try build(.workspace(workspacePath),
|
||||||
|
target: target,
|
||||||
|
into: outputDirectory)
|
||||||
|
}
|
||||||
|
|
||||||
|
public func build(projectPath: AbsolutePath,
|
||||||
|
target: Target,
|
||||||
|
into outputDirectory: AbsolutePath) throws
|
||||||
|
{
|
||||||
|
try build(.project(projectPath),
|
||||||
|
target: target,
|
||||||
|
into: outputDirectory)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Fileprivate
|
||||||
|
|
||||||
|
// swiftlint:disable:next function_body_length
|
||||||
|
fileprivate func build(_ projectTarget: XcodeBuildTarget,
|
||||||
|
target: Target,
|
||||||
|
into outputDirectory: AbsolutePath) throws
|
||||||
|
{
|
||||||
|
guard target.product.isFramework else {
|
||||||
|
throw CacheBinaryBuilderError.nonFrameworkTargetForXCFramework(target.name)
|
||||||
|
}
|
||||||
|
let scheme = target.name.spm_shellEscaped()
|
||||||
|
|
||||||
|
// Create temporary directories
|
||||||
|
return try FileHandler.shared.inTemporaryDirectory { temporaryDirectory in
|
||||||
|
logger.notice("Building .xcframework for \(target.name)...", metadata: .section)
|
||||||
|
|
||||||
|
// Build for the simulator
|
||||||
|
var simulatorArchivePath: AbsolutePath?
|
||||||
|
if target.platform.hasSimulators {
|
||||||
|
simulatorArchivePath = temporaryDirectory.appending(component: "simulator.xcarchive")
|
||||||
|
try simulatorBuild(
|
||||||
|
projectTarget: projectTarget,
|
||||||
|
scheme: scheme,
|
||||||
|
target: target,
|
||||||
|
archivePath: simulatorArchivePath!
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build for the device - if required
|
||||||
|
let deviceArchivePath = temporaryDirectory.appending(component: "device.xcarchive")
|
||||||
|
try deviceBuild(
|
||||||
|
projectTarget: projectTarget,
|
||||||
|
scheme: scheme,
|
||||||
|
target: target,
|
||||||
|
archivePath: deviceArchivePath
|
||||||
|
)
|
||||||
|
|
||||||
|
// Build the xcframework
|
||||||
|
var frameworkpaths = [AbsolutePath]()
|
||||||
|
if let simulatorArchivePath = simulatorArchivePath {
|
||||||
|
frameworkpaths.append(frameworkPath(fromArchivePath: simulatorArchivePath, productName: target.productName))
|
||||||
|
}
|
||||||
|
frameworkpaths.append(frameworkPath(fromArchivePath: deviceArchivePath, productName: target.productName))
|
||||||
|
let xcframeworkPath = outputDirectory.appending(component: "\(target.productName).xcframework")
|
||||||
|
try buildXCFramework(frameworks: frameworkpaths, output: xcframeworkPath, target: target)
|
||||||
|
|
||||||
|
try FileHandler.shared.move(from: xcframeworkPath, to: outputDirectory.appending(component: xcframeworkPath.basename))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fileprivate func buildXCFramework(frameworks: [AbsolutePath], output: AbsolutePath, target: Target) throws {
|
||||||
|
_ = try xcodeBuildController.createXCFramework(frameworks: frameworks, output: output)
|
||||||
|
.do(onSubscribed: {
|
||||||
|
logger.notice("Exporting xcframework for \(target.platform.caseValue)", metadata: .subsection)
|
||||||
|
})
|
||||||
|
.toBlocking()
|
||||||
|
.single()
|
||||||
|
}
|
||||||
|
|
||||||
|
fileprivate func deviceBuild(projectTarget: XcodeBuildTarget,
|
||||||
|
scheme: String,
|
||||||
|
target: Target,
|
||||||
|
archivePath: AbsolutePath) throws
|
||||||
|
{
|
||||||
|
// Without the BUILD_LIBRARY_FOR_DISTRIBUTION argument xcodebuild doesn't generate the .swiftinterface file
|
||||||
|
_ = try xcodeBuildController.archive(projectTarget,
|
||||||
|
scheme: scheme,
|
||||||
|
clean: false,
|
||||||
|
archivePath: archivePath,
|
||||||
|
arguments: [
|
||||||
|
.sdk(target.platform.xcodeDeviceSDK),
|
||||||
|
.buildSetting("SKIP_INSTALL", "NO"),
|
||||||
|
.buildSetting("BUILD_LIBRARY_FOR_DISTRIBUTION", "YES"),
|
||||||
|
])
|
||||||
|
.printFormattedOutput()
|
||||||
|
.do(onSubscribed: {
|
||||||
|
logger.notice("Building \(target.name) for device...", metadata: .subsection)
|
||||||
|
})
|
||||||
|
.ignoreElements()
|
||||||
|
.toBlocking()
|
||||||
|
.last()
|
||||||
|
}
|
||||||
|
|
||||||
|
fileprivate func simulatorBuild(projectTarget: XcodeBuildTarget,
|
||||||
|
scheme: String,
|
||||||
|
target: Target,
|
||||||
|
archivePath: AbsolutePath) throws
|
||||||
|
{
|
||||||
|
// Without the BUILD_LIBRARY_FOR_DISTRIBUTION argument xcodebuild doesn't generate the .swiftinterface file
|
||||||
|
_ = try xcodeBuildController.archive(projectTarget,
|
||||||
|
scheme: scheme,
|
||||||
|
clean: false,
|
||||||
|
archivePath: archivePath,
|
||||||
|
arguments: [
|
||||||
|
.sdk(target.platform.xcodeSimulatorSDK!),
|
||||||
|
.buildSetting("SKIP_INSTALL", "NO"),
|
||||||
|
.buildSetting("BUILD_LIBRARY_FOR_DISTRIBUTION", "YES"),
|
||||||
|
])
|
||||||
|
.printFormattedOutput()
|
||||||
|
.do(onSubscribed: {
|
||||||
|
logger.notice("Building \(target.name) for simulator...", metadata: .subsection)
|
||||||
|
})
|
||||||
|
.ignoreElements()
|
||||||
|
.toBlocking()
|
||||||
|
.last()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the path to the framework inside the archive.
|
||||||
|
/// - Parameters:
|
||||||
|
/// - archivePath: Path to the .xcarchive.
|
||||||
|
/// - productName: Product name.
|
||||||
|
fileprivate func frameworkPath(fromArchivePath archivePath: AbsolutePath, productName: String) -> AbsolutePath {
|
||||||
|
archivePath.appending(RelativePath("Products/Library/Frameworks/\(productName).framework"))
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,189 +0,0 @@
|
||||||
import Foundation
|
|
||||||
import RxSwift
|
|
||||||
import TSCBasic
|
|
||||||
import TuistCore
|
|
||||||
import TuistSupport
|
|
||||||
|
|
||||||
enum XCFrameworkBuilderError: FatalError {
|
|
||||||
case nonFrameworkTarget(String)
|
|
||||||
|
|
||||||
/// Error type.
|
|
||||||
var type: ErrorType {
|
|
||||||
switch self {
|
|
||||||
case .nonFrameworkTarget: return .abort
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Error description.
|
|
||||||
var description: String {
|
|
||||||
switch self {
|
|
||||||
case let .nonFrameworkTarget(name):
|
|
||||||
return "Can't generate an .xcframework from the target '\(name)' because it's not a framework target"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public protocol XCFrameworkBuilding {
|
|
||||||
/// Returns an observable build an xcframework for the given target.
|
|
||||||
/// The target must have framework as product.
|
|
||||||
///
|
|
||||||
/// - Parameters:
|
|
||||||
/// - workspacePath: Path to the generated .xcworkspace that contains the given target.
|
|
||||||
/// - target: Target whose .xcframework will be generated.
|
|
||||||
/// - includeDeviceArch: Define whether the .xcframework will also contain the target built for devices (it only contains the target built for simulators by default).
|
|
||||||
/// - Returns: Path to the compiled .xcframework.
|
|
||||||
func build(workspacePath: AbsolutePath, target: Target, includeDeviceArch: Bool) throws -> Observable<AbsolutePath>
|
|
||||||
|
|
||||||
/// Returns an observable to build an xcframework for the given target.
|
|
||||||
/// The target must have framework as product.
|
|
||||||
///
|
|
||||||
/// - Parameters:
|
|
||||||
/// - projectPath: Path to the generated .xcodeproj that contains the given target.
|
|
||||||
/// - target: Target whose .xcframework will be generated.
|
|
||||||
/// - includeDeviceArch: Define whether the .xcframework will also contain the target built for devices (it only contains the target built for simulators by default).
|
|
||||||
/// - Returns: Path to the compiled .xcframework.
|
|
||||||
func build(projectPath: AbsolutePath, target: Target, includeDeviceArch: Bool) throws -> Observable<AbsolutePath>
|
|
||||||
}
|
|
||||||
|
|
||||||
public final class XCFrameworkBuilder: XCFrameworkBuilding {
|
|
||||||
// MARK: - Attributes
|
|
||||||
|
|
||||||
/// Xcode build controller instance to run xcodebuild commands.
|
|
||||||
private let xcodeBuildController: XcodeBuildControlling
|
|
||||||
|
|
||||||
// MARK: - Init
|
|
||||||
|
|
||||||
/// Initializes the builder.
|
|
||||||
/// - Parameter xcodeBuildController: Xcode build controller instance to run xcodebuild commands.
|
|
||||||
public init(xcodeBuildController: XcodeBuildControlling) {
|
|
||||||
self.xcodeBuildController = xcodeBuildController
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: - XCFrameworkBuilding
|
|
||||||
|
|
||||||
public func build(workspacePath: AbsolutePath, target: Target, includeDeviceArch: Bool) throws -> Observable<AbsolutePath> {
|
|
||||||
try build(.workspace(workspacePath), target: target, includeDeviceArch: includeDeviceArch)
|
|
||||||
}
|
|
||||||
|
|
||||||
public func build(projectPath: AbsolutePath, target: Target, includeDeviceArch: Bool) throws -> Observable<AbsolutePath> {
|
|
||||||
try build(.project(projectPath), target: target, includeDeviceArch: includeDeviceArch)
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: - Fileprivate
|
|
||||||
|
|
||||||
fileprivate func deviceBuild(projectTarget: XcodeBuildTarget,
|
|
||||||
scheme: String,
|
|
||||||
target: Target,
|
|
||||||
deviceArchivePath: AbsolutePath) -> Observable<SystemEvent<XcodeBuildOutput>>
|
|
||||||
{
|
|
||||||
// Without the BUILD_LIBRARY_FOR_DISTRIBUTION argument xcodebuild doesn't generate the .swiftinterface file
|
|
||||||
xcodeBuildController.archive(projectTarget,
|
|
||||||
scheme: scheme,
|
|
||||||
clean: false,
|
|
||||||
archivePath: deviceArchivePath,
|
|
||||||
arguments: [
|
|
||||||
.sdk(target.platform.xcodeDeviceSDK),
|
|
||||||
.buildSetting("SKIP_INSTALL", "NO"),
|
|
||||||
.buildSetting("BUILD_LIBRARY_FOR_DISTRIBUTION", "YES"),
|
|
||||||
])
|
|
||||||
.printFormattedOutput()
|
|
||||||
.do(onSubscribed: {
|
|
||||||
logger.notice("Building \(target.name) for device...", metadata: .subsection)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fileprivate func simulatorBuild(projectTarget: XcodeBuildTarget,
|
|
||||||
scheme: String,
|
|
||||||
target: Target,
|
|
||||||
simulatorArchivePath: AbsolutePath) -> Observable<SystemEvent<XcodeBuildOutput>>
|
|
||||||
{
|
|
||||||
// Without the BUILD_LIBRARY_FOR_DISTRIBUTION argument xcodebuild doesn't generate the .swiftinterface file
|
|
||||||
xcodeBuildController.archive(projectTarget,
|
|
||||||
scheme: scheme,
|
|
||||||
clean: false,
|
|
||||||
archivePath: simulatorArchivePath,
|
|
||||||
arguments: [
|
|
||||||
.sdk(target.platform.xcodeSimulatorSDK!),
|
|
||||||
.buildSetting("SKIP_INSTALL", "NO"),
|
|
||||||
.buildSetting("BUILD_LIBRARY_FOR_DISTRIBUTION", "YES"),
|
|
||||||
])
|
|
||||||
.printFormattedOutput()
|
|
||||||
.do(onSubscribed: {
|
|
||||||
logger.notice("Building \(target.name) for simulator...", metadata: .subsection)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// swiftlint:disable:next function_body_length
|
|
||||||
fileprivate func build(_ projectTarget: XcodeBuildTarget, target: Target, includeDeviceArch: Bool) throws -> Observable<AbsolutePath> {
|
|
||||||
guard target.product.isFramework else {
|
|
||||||
throw XCFrameworkBuilderError.nonFrameworkTarget(target.name)
|
|
||||||
}
|
|
||||||
let scheme = target.name.spm_shellEscaped()
|
|
||||||
|
|
||||||
// Create temporary directories
|
|
||||||
return try withTemporaryDirectories { outputDirectory, temporaryPath in
|
|
||||||
logger.notice("Building .xcframework for \(target.name)...", metadata: .section)
|
|
||||||
|
|
||||||
// Build for the simulator
|
|
||||||
var simulatorArchiveObservable: Observable<SystemEvent<XcodeBuildOutput>>
|
|
||||||
var simulatorArchivePath: AbsolutePath?
|
|
||||||
if target.platform.hasSimulators {
|
|
||||||
simulatorArchivePath = temporaryPath.appending(component: "simulator.xcarchive")
|
|
||||||
simulatorArchiveObservable = simulatorBuild(
|
|
||||||
projectTarget: projectTarget,
|
|
||||||
scheme: scheme,
|
|
||||||
target: target,
|
|
||||||
simulatorArchivePath: simulatorArchivePath!
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
simulatorArchiveObservable = Observable.empty()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Build for the device - if required
|
|
||||||
let deviceArchivePath = temporaryPath.appending(component: "device.xcarchive")
|
|
||||||
var deviceArchiveObservable: Observable<SystemEvent<XcodeBuildOutput>>
|
|
||||||
if includeDeviceArch {
|
|
||||||
deviceArchiveObservable = deviceBuild(
|
|
||||||
projectTarget: projectTarget,
|
|
||||||
scheme: scheme,
|
|
||||||
target: target,
|
|
||||||
deviceArchivePath: deviceArchivePath
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
deviceArchiveObservable = Observable.empty()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Build the xcframework
|
|
||||||
var frameworkpaths: [AbsolutePath] = [AbsolutePath]()
|
|
||||||
if let simulatorArchivePath = simulatorArchivePath {
|
|
||||||
frameworkpaths.append(frameworkPath(fromArchivePath: simulatorArchivePath, productName: target.productName))
|
|
||||||
}
|
|
||||||
if includeDeviceArch {
|
|
||||||
frameworkpaths.append(frameworkPath(fromArchivePath: deviceArchivePath, productName: target.productName))
|
|
||||||
}
|
|
||||||
|
|
||||||
let xcframeworkPath = outputDirectory.appending(component: "\(target.productName).xcframework")
|
|
||||||
let xcframeworkObservable = xcodeBuildController.createXCFramework(frameworks: frameworkpaths, output: xcframeworkPath)
|
|
||||||
.do(onSubscribed: {
|
|
||||||
logger.notice("Exporting xcframework for \(target.platform.caseValue)", metadata: .subsection)
|
|
||||||
})
|
|
||||||
|
|
||||||
return deviceArchiveObservable
|
|
||||||
.concat(simulatorArchiveObservable)
|
|
||||||
.concat(xcframeworkObservable)
|
|
||||||
.ignoreElements()
|
|
||||||
.andThen(Observable.just(xcframeworkPath))
|
|
||||||
.do(afterCompleted: {
|
|
||||||
try FileHandler.shared.delete(temporaryPath)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the path to the framework inside the archive.
|
|
||||||
/// - Parameters:
|
|
||||||
/// - archivePath: Path to the .xcarchive.
|
|
||||||
/// - productName: Product name.
|
|
||||||
fileprivate func frameworkPath(fromArchivePath archivePath: AbsolutePath, productName: String) -> AbsolutePath {
|
|
||||||
archivePath.appending(RelativePath("Products/Library/Frameworks/\(productName).framework"))
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,52 @@
|
||||||
|
import Foundation
|
||||||
|
import RxSwift
|
||||||
|
import TSCBasic
|
||||||
|
import TuistCache
|
||||||
|
import TuistCore
|
||||||
|
import TuistSupportTesting
|
||||||
|
|
||||||
|
public final class MockCacheArtifactBuilder: CacheArtifactBuilding {
|
||||||
|
public init() {}
|
||||||
|
|
||||||
|
public var invokedCacheOutputTypeGetter = false
|
||||||
|
public var invokedCacheOutputTypeGetterCount = 0
|
||||||
|
public var stubbedCacheOutputType: CacheOutputType!
|
||||||
|
|
||||||
|
public var cacheOutputType: CacheOutputType {
|
||||||
|
invokedCacheOutputTypeGetter = true
|
||||||
|
invokedCacheOutputTypeGetterCount += 1
|
||||||
|
return stubbedCacheOutputType
|
||||||
|
}
|
||||||
|
|
||||||
|
public var invokedBuildWorkspacePath = false
|
||||||
|
public var invokedBuildWorkspacePathCount = 0
|
||||||
|
public var invokedBuildWorkspacePathParameters: (workspacePath: AbsolutePath, target: Target, outputDirectory: AbsolutePath)?
|
||||||
|
public var invokedBuildWorkspacePathParametersList = [(workspacePath: AbsolutePath, target: Target, outputDirectory: AbsolutePath)]()
|
||||||
|
public var stubbedBuildWorkspacePathError: Error?
|
||||||
|
|
||||||
|
public func build(workspacePath: AbsolutePath, target: Target, into outputDirectory: AbsolutePath) throws {
|
||||||
|
invokedBuildWorkspacePath = true
|
||||||
|
invokedBuildWorkspacePathCount += 1
|
||||||
|
invokedBuildWorkspacePathParameters = (workspacePath, target, outputDirectory)
|
||||||
|
invokedBuildWorkspacePathParametersList.append((workspacePath, target, outputDirectory))
|
||||||
|
if let error = stubbedBuildWorkspacePathError {
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public var invokedBuildProjectPath = false
|
||||||
|
public var invokedBuildProjectPathCount = 0
|
||||||
|
public var invokedBuildProjectPathParameters: (projectPath: AbsolutePath, target: Target, outputDirectory: AbsolutePath)?
|
||||||
|
public var invokedBuildProjectPathParametersList = [(projectPath: AbsolutePath, target: Target, outputDirectory: AbsolutePath)]()
|
||||||
|
public var stubbedBuildProjectPathError: Error?
|
||||||
|
|
||||||
|
public func build(projectPath: AbsolutePath, target: Target, into outputDirectory: AbsolutePath) throws {
|
||||||
|
invokedBuildProjectPath = true
|
||||||
|
invokedBuildProjectPathCount += 1
|
||||||
|
invokedBuildProjectPathParameters = (projectPath, target, outputDirectory)
|
||||||
|
invokedBuildProjectPathParametersList.append((projectPath, target, outputDirectory))
|
||||||
|
if let error = stubbedBuildProjectPathError {
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -30,10 +30,10 @@ public final class MockCacheStorage: CacheStoring {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var storeStub: ((_ hash: String, _ xcframeworkPath: AbsolutePath) -> Void)?
|
var storeStub: ((_ hash: String, _ paths: [AbsolutePath]) -> Void)?
|
||||||
public func store(hash: String, xcframeworkPath: AbsolutePath) -> Completable {
|
public func store(hash: String, paths: [AbsolutePath]) -> Completable {
|
||||||
if let storeStub = storeStub {
|
if let storeStub = storeStub {
|
||||||
storeStub(hash, xcframeworkPath)
|
storeStub(hash, paths)
|
||||||
}
|
}
|
||||||
return Completable.empty()
|
return Completable.empty()
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,42 +0,0 @@
|
||||||
import Foundation
|
|
||||||
import RxSwift
|
|
||||||
import TSCBasic
|
|
||||||
import TuistCache
|
|
||||||
import TuistCore
|
|
||||||
|
|
||||||
public final class MockXCFrameworkBuilder: XCFrameworkBuilding {
|
|
||||||
public var buildProjectArgs: [(projectPath: AbsolutePath, target: Target, includeDeviceArch: Bool)] = []
|
|
||||||
public var buildWorkspaceArgs: [(workspacePath: AbsolutePath, target: Target, includeDeviceArch: Bool)] = []
|
|
||||||
public var buildProjectStub: ((AbsolutePath, Target) -> Result<AbsolutePath, Error>)?
|
|
||||||
public var buildWorkspaceStub: ((AbsolutePath, Target) -> Result<AbsolutePath, Error>)?
|
|
||||||
|
|
||||||
public init() {}
|
|
||||||
|
|
||||||
public func build(projectPath: AbsolutePath, target: Target, includeDeviceArch: Bool) throws -> Observable<AbsolutePath> {
|
|
||||||
buildProjectArgs.append((projectPath: projectPath, target: target, includeDeviceArch: includeDeviceArch))
|
|
||||||
if let buildProjectStub = buildProjectStub {
|
|
||||||
switch buildProjectStub(projectPath, target) {
|
|
||||||
case let .failure(error):
|
|
||||||
return Observable.error(error)
|
|
||||||
case let .success(path):
|
|
||||||
return Observable.just(path)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return Observable.just(AbsolutePath.root)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public func build(workspacePath: AbsolutePath, target: Target, includeDeviceArch: Bool) throws -> Observable<AbsolutePath> {
|
|
||||||
buildWorkspaceArgs.append((workspacePath: workspacePath, target: target, includeDeviceArch: includeDeviceArch))
|
|
||||||
if let buildWorkspaceStub = buildWorkspaceStub {
|
|
||||||
switch buildWorkspaceStub(workspacePath, target) {
|
|
||||||
case let .failure(error):
|
|
||||||
return Observable.error(error)
|
|
||||||
case let .success(path):
|
|
||||||
return Observable.just(path)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return Observable.just(AbsolutePath.root)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -3,23 +3,23 @@ import TuistCore
|
||||||
@testable import TuistCache
|
@testable import TuistCache
|
||||||
|
|
||||||
public final class MockGraphContentHasher: GraphContentHashing {
|
public final class MockGraphContentHasher: GraphContentHashing {
|
||||||
public init() {}
|
|
||||||
|
|
||||||
public var invokedContentHashes = false
|
public var invokedContentHashes = false
|
||||||
public var invokedContentHashesCount = 0
|
public var invokedContentHashesCount = 0
|
||||||
public var invokedContentHashesParameters: (graph: TuistCore.Graph, Void)?
|
public var invokedContentHashesParameters: (graph: TuistCore.Graph, cacheOutputType: CacheOutputType)?
|
||||||
public var invokedContentHashesParametersList = [(graph: TuistCore.Graph, Void)]()
|
public var invokedContentHashesParametersList = [(graph: TuistCore.Graph, cacheOutputType: CacheOutputType)]()
|
||||||
public var stubbedContentHashesError: Error?
|
public var stubbedContentHashesError: Error?
|
||||||
public var contentHashesStub: [TargetNode: String]! = [:]
|
public var stubbedContentHashesResult: [TargetNode: String]! = [:]
|
||||||
|
|
||||||
public func contentHashes(for graph: TuistCore.Graph) throws -> [TargetNode: String] {
|
public init() {}
|
||||||
|
|
||||||
|
public func contentHashes(for graph: TuistCore.Graph, cacheOutputType: CacheOutputType) throws -> [TargetNode: String] {
|
||||||
invokedContentHashes = true
|
invokedContentHashes = true
|
||||||
invokedContentHashesCount += 1
|
invokedContentHashesCount += 1
|
||||||
invokedContentHashesParameters = (graph, ())
|
invokedContentHashesParameters = (graph, cacheOutputType)
|
||||||
invokedContentHashesParametersList.append((graph, ()))
|
invokedContentHashesParametersList.append((graph, cacheOutputType))
|
||||||
if let error = stubbedContentHashesError {
|
if let error = stubbedContentHashesError {
|
||||||
throw error
|
throw error
|
||||||
}
|
}
|
||||||
return contentHashesStub
|
return stubbedContentHashesResult
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,11 @@ import RxSwift
|
||||||
import TSCBasic
|
import TSCBasic
|
||||||
import TuistSupport
|
import TuistSupport
|
||||||
|
|
||||||
|
public enum XcodeBuildDestination: Equatable {
|
||||||
|
case device(String)
|
||||||
|
case mac
|
||||||
|
}
|
||||||
|
|
||||||
public protocol XcodeBuildControlling {
|
public protocol XcodeBuildControlling {
|
||||||
/// Returns an observable to build the given project using xcodebuild.
|
/// Returns an observable to build the given project using xcodebuild.
|
||||||
/// - Parameters:
|
/// - Parameters:
|
||||||
|
@ -15,6 +20,20 @@ public protocol XcodeBuildControlling {
|
||||||
clean: Bool,
|
clean: Bool,
|
||||||
arguments: [XcodeBuildArgument]) -> Observable<SystemEvent<XcodeBuildOutput>>
|
arguments: [XcodeBuildArgument]) -> Observable<SystemEvent<XcodeBuildOutput>>
|
||||||
|
|
||||||
|
/// Returns an observable to test the given project using xcodebuild.
|
||||||
|
/// - Parameters:
|
||||||
|
/// - target: The project or workspace to be built.
|
||||||
|
/// - scheme: The scheme of the project that should be built.
|
||||||
|
/// - clean: True if xcodebuild should clean the project before building.
|
||||||
|
/// - arguments: Extra xcodebuild arguments.
|
||||||
|
func test(
|
||||||
|
_ target: XcodeBuildTarget,
|
||||||
|
scheme: String,
|
||||||
|
clean: Bool,
|
||||||
|
destination: XcodeBuildDestination,
|
||||||
|
arguments: [XcodeBuildArgument]
|
||||||
|
) -> Observable<SystemEvent<XcodeBuildOutput>>
|
||||||
|
|
||||||
/// Returns an observable that archives the given project using xcodebuild.
|
/// Returns an observable that archives the given project using xcodebuild.
|
||||||
/// - Parameters:
|
/// - Parameters:
|
||||||
/// - target: The project or workspace to be archived.
|
/// - target: The project or workspace to be archived.
|
||||||
|
|
|
@ -0,0 +1,19 @@
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
/// An enum that represents the type of output that the caching feature can work with.
|
||||||
|
public enum CacheOutputType: CustomStringConvertible {
|
||||||
|
/// Frameworks built for the simulator.
|
||||||
|
case framework
|
||||||
|
|
||||||
|
/// XCFrameworks built for the simulator and device.
|
||||||
|
case xcframework
|
||||||
|
|
||||||
|
public var description: String {
|
||||||
|
switch self {
|
||||||
|
case .framework:
|
||||||
|
return "framework"
|
||||||
|
case .xcframework:
|
||||||
|
return "xcframework"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -33,7 +33,10 @@ public class Graph: Encodable, Equatable {
|
||||||
/// The entry nodes of the graph.
|
/// The entry nodes of the graph.
|
||||||
public let entryNodes: [GraphNode]
|
public let entryNodes: [GraphNode]
|
||||||
|
|
||||||
/// Dictionary whose keys are paths to directories where projects are defined, and the values are the representation of the projects.
|
/// Workspace of the graph
|
||||||
|
public let workspace: Workspace
|
||||||
|
|
||||||
|
/// Projects of the graph
|
||||||
public let projects: [Project]
|
public let projects: [Project]
|
||||||
|
|
||||||
/// Dictionary whose keys are paths to directories where projects are defined, and the values are the CocoaPods nodes define in them.
|
/// Dictionary whose keys are paths to directories where projects are defined, and the values are the CocoaPods nodes define in them.
|
||||||
|
@ -54,31 +57,48 @@ public class Graph: Encodable, Equatable {
|
||||||
/// Dictionary whose keys are path to directories where projects are defined, and the values are target nodes defined in them.
|
/// Dictionary whose keys are path to directories where projects are defined, and the values are target nodes defined in them.
|
||||||
public let targets: [AbsolutePath: [TargetNode]]
|
public let targets: [AbsolutePath: [TargetNode]]
|
||||||
|
|
||||||
|
/// Schemes of the graph
|
||||||
|
public var schemes: [Scheme] {
|
||||||
|
projects.flatMap(\.schemes) + workspace.schemes
|
||||||
|
}
|
||||||
|
|
||||||
// MARK: - Init
|
// MARK: - Init
|
||||||
|
|
||||||
convenience init(name: String, entryPath: AbsolutePath, cache: GraphLoaderCaching, entryNodes: [GraphNode]) {
|
convenience init(
|
||||||
self.init(name: name,
|
name: String,
|
||||||
|
entryPath: AbsolutePath,
|
||||||
|
cache: GraphLoaderCaching,
|
||||||
|
entryNodes: [GraphNode],
|
||||||
|
workspace: Workspace
|
||||||
|
) {
|
||||||
|
self.init(
|
||||||
|
name: name,
|
||||||
entryPath: entryPath,
|
entryPath: entryPath,
|
||||||
entryNodes: entryNodes,
|
entryNodes: entryNodes,
|
||||||
|
workspace: workspace,
|
||||||
projects: Array(cache.projects.values),
|
projects: Array(cache.projects.values),
|
||||||
cocoapods: Array(cache.cocoapodsNodes.values),
|
cocoapods: Array(cache.cocoapodsNodes.values),
|
||||||
packages: Array(cache.packages.flatMap { $0.value }),
|
packages: Array(cache.packages.flatMap { $0.value }),
|
||||||
precompiled: Array(cache.precompiledNodes.values),
|
precompiled: Array(cache.precompiledNodes.values),
|
||||||
targets: cache.targetNodes.mapValues { Array($0.values) })
|
targets: cache.targetNodes.mapValues { Array($0.values) }
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
public init(name: String,
|
public init(
|
||||||
|
name: String,
|
||||||
entryPath: AbsolutePath,
|
entryPath: AbsolutePath,
|
||||||
entryNodes: [GraphNode],
|
entryNodes: [GraphNode],
|
||||||
|
workspace: Workspace,
|
||||||
projects: [Project],
|
projects: [Project],
|
||||||
cocoapods: [CocoaPodsNode],
|
cocoapods: [CocoaPodsNode],
|
||||||
packages: [PackageNode],
|
packages: [PackageNode],
|
||||||
precompiled: [PrecompiledNode],
|
precompiled: [PrecompiledNode],
|
||||||
targets: [AbsolutePath: [TargetNode]])
|
targets: [AbsolutePath: [TargetNode]]
|
||||||
{
|
) {
|
||||||
self.name = name
|
self.name = name
|
||||||
self.entryPath = entryPath
|
self.entryPath = entryPath
|
||||||
self.entryNodes = entryNodes
|
self.entryNodes = entryNodes
|
||||||
|
self.workspace = workspace
|
||||||
self.projects = projects
|
self.projects = projects
|
||||||
self.cocoapods = cocoapods
|
self.cocoapods = cocoapods
|
||||||
self.packages = packages
|
self.packages = packages
|
||||||
|
@ -186,7 +206,7 @@ public class Graph: Encodable, Equatable {
|
||||||
/// - Parameters:
|
/// - Parameters:
|
||||||
/// - path: Path to the directory where the project that defines the target is located.
|
/// - path: Path to the directory where the project that defines the target is located.
|
||||||
/// - name: Name of the target.
|
/// - name: Name of the target.
|
||||||
public func linkableDependencies(path: AbsolutePath, name: String) -> [GraphDependencyReference] {
|
public func linkableDependencies(path: AbsolutePath, name: String) throws -> [GraphDependencyReference] {
|
||||||
guard let targetNode = findTargetNode(path: path, name: name) else {
|
guard let targetNode = findTargetNode(path: path, name: name) else {
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
|
@ -205,6 +225,13 @@ public class Graph: Encodable, Equatable {
|
||||||
references = references.union(transitiveSystemLibraries)
|
references = references.union(transitiveSystemLibraries)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if targetNode.target.isAppClip {
|
||||||
|
let path = try SDKNode.appClip(status: .required).path
|
||||||
|
references.insert(GraphDependencyReference.sdk(path: path,
|
||||||
|
status: .required,
|
||||||
|
source: .system))
|
||||||
|
}
|
||||||
|
|
||||||
let directSystemLibrariesAndFrameworks = targetNode.sdkDependencies.map {
|
let directSystemLibrariesAndFrameworks = targetNode.sdkDependencies.map {
|
||||||
GraphDependencyReference.sdk(path: $0.path, status: $0.status, source: $0.source)
|
GraphDependencyReference.sdk(path: $0.path, status: $0.status, source: $0.source)
|
||||||
}
|
}
|
||||||
|
@ -357,7 +384,7 @@ public class Graph: Encodable, Equatable {
|
||||||
if let hostApp = hostApplication(for: targetNode) {
|
if let hostApp = hostApplication(for: targetNode) {
|
||||||
references.subtract(try embeddableFrameworks(path: hostApp.path, name: hostApp.name))
|
references.subtract(try embeddableFrameworks(path: hostApp.path, name: hostApp.name))
|
||||||
} else {
|
} else {
|
||||||
references.subtract(precompiledFrameworks)
|
references = []
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -387,7 +414,7 @@ public class Graph: Encodable, Equatable {
|
||||||
/// - Parameter project: Project whose dependency references will be returned.
|
/// - Parameter project: Project whose dependency references will be returned.
|
||||||
public func allDependencyReferences(for project: Project) throws -> [GraphDependencyReference] {
|
public func allDependencyReferences(for project: Project) throws -> [GraphDependencyReference] {
|
||||||
let linkableDependencies = try project.targets.flatMap {
|
let linkableDependencies = try project.targets.flatMap {
|
||||||
self.linkableDependencies(path: project.path, name: $0.name)
|
try self.linkableDependencies(path: project.path, name: $0.name)
|
||||||
}
|
}
|
||||||
|
|
||||||
let embeddableDependencies = try project.targets.flatMap {
|
let embeddableDependencies = try project.targets.flatMap {
|
||||||
|
@ -419,6 +446,14 @@ public class Graph: Encodable, Equatable {
|
||||||
.filter { validProducts.contains($0.target.product) }
|
.filter { validProducts.contains($0.target.product) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public func appClipsDependency(path: AbsolutePath, name: String) -> TargetNode? {
|
||||||
|
guard let targetNode = findTargetNode(path: path, name: name) else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return targetNode.targetDependencies.first { $0.target.product == .appClip }
|
||||||
|
}
|
||||||
|
|
||||||
/// Depth-first search (DFS) is an algorithm for traversing graph data structures. It starts at a source node
|
/// Depth-first search (DFS) is an algorithm for traversing graph data structures. It starts at a source node
|
||||||
/// and explores as far as possible along each branch before backtracking.
|
/// and explores as far as possible along each branch before backtracking.
|
||||||
///
|
///
|
||||||
|
@ -475,7 +510,7 @@ public class Graph: Encodable, Equatable {
|
||||||
stack.push(child)
|
stack.push(child)
|
||||||
}
|
}
|
||||||
} else if let frameworkNode = node as? FrameworkNode {
|
} else if let frameworkNode = node as? FrameworkNode {
|
||||||
for child in frameworkNode.dependencies where !visited.contains(child) {
|
for child in frameworkNode.dependencies.map(\.node) where !visited.contains(child) {
|
||||||
stack.push(child)
|
stack.push(child)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -531,31 +566,60 @@ public class Graph: Encodable, Equatable {
|
||||||
} ?? nil
|
} ?? nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// - Returns: Host application for a given `targetNode`, if it exists
|
||||||
|
public func hostApplication(for targetNode: TargetNode) -> TargetNode? {
|
||||||
|
targetDependencies(path: targetNode.path, name: targetNode.name)
|
||||||
|
.first(where: { $0.target.product == .app })
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns a copy of the graph with the given projects set.
|
/// Returns a copy of the graph with the given projects set.
|
||||||
/// - Parameter projects: Projects to be set to the copy.
|
/// - Parameter projects: Projects to be set to the copy.
|
||||||
public func with(projects: [Project]) -> Graph {
|
public func with(projects: [Project]) -> Graph {
|
||||||
Graph(name: name,
|
Graph(
|
||||||
|
name: name,
|
||||||
entryPath: entryPath,
|
entryPath: entryPath,
|
||||||
entryNodes: entryNodes,
|
entryNodes: entryNodes,
|
||||||
|
workspace: workspace,
|
||||||
projects: projects,
|
projects: projects,
|
||||||
cocoapods: cocoapods,
|
cocoapods: cocoapods,
|
||||||
packages: packages,
|
packages: packages,
|
||||||
precompiled: precompiled,
|
precompiled: precompiled,
|
||||||
targets: targets)
|
targets: targets
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a copy of the graph with the given targets.
|
/// Returns a copy of the graph with the given targets.
|
||||||
/// - Parameter targets: Targets to be set to the copy.
|
/// - Parameter targets: Targets to be set to the copy.
|
||||||
/// - Returns: New graph with the given targets.
|
/// - Returns: New graph with the given targets.
|
||||||
public func with(targets: [AbsolutePath: [TargetNode]]) -> Graph {
|
public func with(targets: [AbsolutePath: [TargetNode]]) -> Graph {
|
||||||
Graph(name: name,
|
Graph(
|
||||||
|
name: name,
|
||||||
entryPath: entryPath,
|
entryPath: entryPath,
|
||||||
entryNodes: entryNodes,
|
entryNodes: entryNodes,
|
||||||
|
workspace: workspace,
|
||||||
projects: projects,
|
projects: projects,
|
||||||
cocoapods: cocoapods,
|
cocoapods: cocoapods,
|
||||||
packages: packages,
|
packages: packages,
|
||||||
precompiled: precompiled,
|
precompiled: precompiled,
|
||||||
targets: targets)
|
targets: targets
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a copy of the graph with a given workspace.
|
||||||
|
/// - Parameter workspace: Workspace to be set to the copy.
|
||||||
|
/// - Returns: New graph with a given workspace.
|
||||||
|
public func with(workspace: Workspace) -> Graph {
|
||||||
|
Graph(
|
||||||
|
name: name,
|
||||||
|
entryPath: entryPath,
|
||||||
|
entryNodes: entryNodes,
|
||||||
|
workspace: workspace,
|
||||||
|
projects: projects,
|
||||||
|
cocoapods: cocoapods,
|
||||||
|
packages: packages,
|
||||||
|
precompiled: precompiled,
|
||||||
|
targets: targets
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func forEach(closure: (GraphNode) -> Void) {
|
public func forEach(closure: (GraphNode) -> Void) {
|
||||||
|
@ -587,8 +651,8 @@ public class Graph: Encodable, Equatable {
|
||||||
stack.push(child)
|
stack.push(child)
|
||||||
}
|
}
|
||||||
} else if let frameworkNode = node as? FrameworkNode {
|
} else if let frameworkNode = node as? FrameworkNode {
|
||||||
for child in frameworkNode.dependencies where !visited.contains(child) {
|
for child in frameworkNode.dependencies where !visited.contains(child.node) {
|
||||||
stack.push(child)
|
stack.push(child.node)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -600,11 +664,6 @@ public class Graph: Encodable, Equatable {
|
||||||
.product(target: targetNode.target.name, productName: targetNode.target.productNameWithExtension)
|
.product(target: targetNode.target.name, productName: targetNode.target.productNameWithExtension)
|
||||||
}
|
}
|
||||||
|
|
||||||
fileprivate func hostApplication(for targetNode: TargetNode) -> TargetNode? {
|
|
||||||
targetDependencies(path: targetNode.path, name: targetNode.name)
|
|
||||||
.first(where: { $0.target.product == .app })
|
|
||||||
}
|
|
||||||
|
|
||||||
fileprivate func isStaticLibrary(targetNode: TargetNode) -> Bool {
|
fileprivate func isStaticLibrary(targetNode: TargetNode) -> Bool {
|
||||||
targetNode.target.product.isStatic
|
targetNode.target.product.isStatic
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,7 +9,7 @@ public protocol GraphLoading: AnyObject {
|
||||||
|
|
||||||
/// Loads the graph for the workspace in the given directory.
|
/// Loads the graph for the workspace in the given directory.
|
||||||
/// - Parameter path: Path to the directory that contains the workspace.
|
/// - Parameter path: Path to the directory that contains the workspace.
|
||||||
func loadWorkspace(path: AbsolutePath) throws -> (Graph, Workspace)
|
func loadWorkspace(path: AbsolutePath) throws -> Graph
|
||||||
|
|
||||||
/// Loads the configuration.
|
/// Loads the configuration.
|
||||||
///
|
///
|
||||||
|
@ -64,15 +64,19 @@ public class GraphLoader: GraphLoading {
|
||||||
let entryNodes: [GraphNode] = try project.targets.map { target in
|
let entryNodes: [GraphNode] = try project.targets.map { target in
|
||||||
try self.loadTarget(name: target.name, path: path, graphLoaderCache: graphLoaderCache, graphCircularDetector: graphCircularDetector)
|
try self.loadTarget(name: target.name, path: path, graphLoaderCache: graphLoaderCache, graphCircularDetector: graphCircularDetector)
|
||||||
}
|
}
|
||||||
|
let workspace = Workspace(path: project.path, name: project.name, projects: [project.path])
|
||||||
|
|
||||||
let graph = Graph(name: project.name,
|
let graph = Graph(
|
||||||
|
name: project.name,
|
||||||
entryPath: path,
|
entryPath: path,
|
||||||
cache: graphLoaderCache,
|
cache: graphLoaderCache,
|
||||||
entryNodes: entryNodes)
|
entryNodes: entryNodes,
|
||||||
|
workspace: workspace
|
||||||
|
)
|
||||||
return (graph, project)
|
return (graph, project)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func loadWorkspace(path: AbsolutePath) throws -> (Graph, Workspace) {
|
public func loadWorkspace(path: AbsolutePath) throws -> Graph {
|
||||||
let graphLoaderCache = GraphLoaderCache()
|
let graphLoaderCache = GraphLoaderCache()
|
||||||
let graphCircularDetector = GraphCircularDetector()
|
let graphCircularDetector = GraphCircularDetector()
|
||||||
let workspace = try modelLoader.loadWorkspace(at: path)
|
let workspace = try modelLoader.loadWorkspace(at: path)
|
||||||
|
@ -92,11 +96,14 @@ public class GraphLoader: GraphLoading {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let graph = Graph(name: workspace.name,
|
let graph = Graph(
|
||||||
|
name: workspace.name,
|
||||||
entryPath: path,
|
entryPath: path,
|
||||||
cache: graphLoaderCache,
|
cache: graphLoaderCache,
|
||||||
entryNodes: entryNodes)
|
entryNodes: entryNodes,
|
||||||
return (graph, workspace)
|
workspace: workspace
|
||||||
|
)
|
||||||
|
return graph
|
||||||
}
|
}
|
||||||
|
|
||||||
public func loadConfig(path: AbsolutePath) throws -> Config {
|
public func loadConfig(path: AbsolutePath) throws -> Config {
|
||||||
|
|
|
@ -34,4 +34,8 @@ public final class GraphTraverser: GraphTraversing {
|
||||||
public func directStaticDependencies(path: AbsolutePath, name: String) -> [GraphDependencyReference] {
|
public func directStaticDependencies(path: AbsolutePath, name: String) -> [GraphDependencyReference] {
|
||||||
graph.staticDependencies(path: path, name: name)
|
graph.staticDependencies(path: path, name: name)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public func appClipsDependency(path: AbsolutePath, name: String) -> Target? {
|
||||||
|
graph.appClipsDependency(path: path, name: name).map { $0.target }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,10 +24,16 @@ public class TargetNodeGraphMapper: GraphMapping {
|
||||||
map(node: $0, mappedCache: &mappedCache, cache: cache)
|
map(node: $0, mappedCache: &mappedCache, cache: cache)
|
||||||
}
|
}
|
||||||
|
|
||||||
return (Graph(name: graph.name,
|
return (
|
||||||
|
Graph(
|
||||||
|
name: graph.name,
|
||||||
entryPath: graph.entryPath,
|
entryPath: graph.entryPath,
|
||||||
cache: cache,
|
cache: cache,
|
||||||
entryNodes: updatedNodes), [])
|
entryNodes: updatedNodes,
|
||||||
|
workspace: graph.workspace
|
||||||
|
),
|
||||||
|
[]
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Private
|
// MARK: - Private
|
||||||
|
|
|
@ -0,0 +1,51 @@
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
public struct WorkspaceWithProjects {
|
||||||
|
public var workspace: Workspace
|
||||||
|
public var projects: [Project]
|
||||||
|
public init(workspace: Workspace, projects: [Project]) {
|
||||||
|
self.workspace = workspace
|
||||||
|
self.projects = projects
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public protocol WorkspaceMapping {
|
||||||
|
func map(workspace: WorkspaceWithProjects) throws -> (WorkspaceWithProjects, [SideEffectDescriptor])
|
||||||
|
}
|
||||||
|
|
||||||
|
public final class SequentialWorkspaceMapper: WorkspaceMapping {
|
||||||
|
let mappers: [WorkspaceMapping]
|
||||||
|
|
||||||
|
public init(mappers: [WorkspaceMapping]) {
|
||||||
|
self.mappers = mappers
|
||||||
|
}
|
||||||
|
|
||||||
|
public func map(workspace: WorkspaceWithProjects) throws -> (WorkspaceWithProjects, [SideEffectDescriptor]) {
|
||||||
|
var results = (workspace: workspace, sideEffects: [SideEffectDescriptor]())
|
||||||
|
results = try mappers.reduce(into: results) { results, mapper in
|
||||||
|
let (updatedWorkspace, sideEffects) = try mapper.map(workspace: results.workspace)
|
||||||
|
results.workspace = updatedWorkspace
|
||||||
|
results.sideEffects.append(contentsOf: sideEffects)
|
||||||
|
}
|
||||||
|
return results
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public final class ProjectWorkspaceMapper: WorkspaceMapping {
|
||||||
|
private let mapper: ProjectMapping
|
||||||
|
public init(mapper: ProjectMapping) {
|
||||||
|
self.mapper = mapper
|
||||||
|
}
|
||||||
|
|
||||||
|
public func map(workspace: WorkspaceWithProjects) throws -> (WorkspaceWithProjects, [SideEffectDescriptor]) {
|
||||||
|
var results = (projects: [Project](), sideEffects: [SideEffectDescriptor]())
|
||||||
|
results = try workspace.projects.reduce(into: results) { results, project in
|
||||||
|
let (updatedProject, sideEffects) = try mapper.map(project: project)
|
||||||
|
results.projects.append(updatedProject)
|
||||||
|
results.sideEffects.append(contentsOf: sideEffects)
|
||||||
|
}
|
||||||
|
let updatedWorkspace = WorkspaceWithProjects(workspace: workspace.workspace,
|
||||||
|
projects: results.projects)
|
||||||
|
return (updatedWorkspace, results.sideEffects)
|
||||||
|
}
|
||||||
|
}
|
|
@ -15,9 +15,6 @@ public class FrameworkNode: PrecompiledNode {
|
||||||
/// The architectures supported by the binary.
|
/// The architectures supported by the binary.
|
||||||
public let architectures: [BinaryArchitecture]
|
public let architectures: [BinaryArchitecture]
|
||||||
|
|
||||||
/// Framework dependencies.
|
|
||||||
public let dependencies: [FrameworkNode]
|
|
||||||
|
|
||||||
/// Returns the type of product.
|
/// Returns the type of product.
|
||||||
public var product: Product {
|
public var product: Product {
|
||||||
if linking == .static {
|
if linking == .static {
|
||||||
|
@ -42,14 +39,13 @@ public class FrameworkNode: PrecompiledNode {
|
||||||
bcsymbolmapPaths: [AbsolutePath],
|
bcsymbolmapPaths: [AbsolutePath],
|
||||||
linking: BinaryLinking,
|
linking: BinaryLinking,
|
||||||
architectures: [BinaryArchitecture] = [],
|
architectures: [BinaryArchitecture] = [],
|
||||||
dependencies: [FrameworkNode] = [])
|
dependencies: [Dependency] = [])
|
||||||
{
|
{
|
||||||
self.dsymPath = dsymPath
|
self.dsymPath = dsymPath
|
||||||
self.bcsymbolmapPaths = bcsymbolmapPaths
|
self.bcsymbolmapPaths = bcsymbolmapPaths
|
||||||
self.linking = linking
|
self.linking = linking
|
||||||
self.architectures = architectures
|
self.architectures = architectures
|
||||||
self.dependencies = dependencies
|
super.init(path: path, dependencies: dependencies)
|
||||||
super.init(path: path)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override public func encode(to encoder: Encoder) throws {
|
override public func encode(to encoder: Encoder) throws {
|
||||||
|
|
|
@ -2,7 +2,7 @@ import Foundation
|
||||||
import TSCBasic
|
import TSCBasic
|
||||||
import TuistSupport
|
import TuistSupport
|
||||||
|
|
||||||
public class GraphNode: Equatable, Hashable, Encodable, CustomStringConvertible {
|
public class GraphNode: Equatable, Hashable, Encodable, CustomStringConvertible, CustomDebugStringConvertible {
|
||||||
// MARK: - Attributes
|
// MARK: - Attributes
|
||||||
|
|
||||||
/// The path to the node.
|
/// The path to the node.
|
||||||
|
@ -14,6 +14,9 @@ public class GraphNode: Equatable, Hashable, Encodable, CustomStringConvertible
|
||||||
/// The description of the node.
|
/// The description of the node.
|
||||||
public var description: String { name }
|
public var description: String { name }
|
||||||
|
|
||||||
|
/// The debug description of the node.
|
||||||
|
public var debugDescription: String { name }
|
||||||
|
|
||||||
// MARK: - Init
|
// MARK: - Init
|
||||||
|
|
||||||
public init(path: AbsolutePath, name: String) {
|
public init(path: AbsolutePath, name: String) {
|
||||||
|
|
|
@ -3,11 +3,37 @@ import TSCBasic
|
||||||
import TuistSupport
|
import TuistSupport
|
||||||
|
|
||||||
public class PrecompiledNode: GraphNode {
|
public class PrecompiledNode: GraphNode {
|
||||||
public init(path: AbsolutePath) {
|
/// It represents a dependency of a precompiled node, which can be either a framework, or another .xcframework.
|
||||||
|
public enum Dependency: Equatable, Hashable {
|
||||||
|
case framework(FrameworkNode)
|
||||||
|
case xcframework(XCFrameworkNode)
|
||||||
|
|
||||||
|
/// Path to the dependency.
|
||||||
|
public var path: AbsolutePath {
|
||||||
|
switch self {
|
||||||
|
case let .framework(framework): return framework.path
|
||||||
|
case let .xcframework(xcframework): return xcframework.path
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the node that represents the dependency.
|
||||||
|
public var node: PrecompiledNode {
|
||||||
|
switch self {
|
||||||
|
case let .framework(framework): return framework
|
||||||
|
case let .xcframework(xcframework): return xcframework
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// List of other precompiled artifacts this precompiled node depends on.
|
||||||
|
public private(set) var dependencies: [Dependency]
|
||||||
|
|
||||||
|
public init(path: AbsolutePath, dependencies: [Dependency] = []) {
|
||||||
/// Returns the name of the precompiled node removing the extension
|
/// Returns the name of the precompiled node removing the extension
|
||||||
/// Alamofire.framework -> Alamofire
|
/// Alamofire.framework -> Alamofire
|
||||||
/// libAlamofire.a -> libAlamofire
|
/// libAlamofire.a -> libAlamofire
|
||||||
let name = String(path.components.last!.split(separator: ".").first!)
|
let name = String(path.components.last!.split(separator: ".").first!)
|
||||||
|
self.dependencies = dependencies
|
||||||
super.init(path: path, name: name)
|
super.init(path: path, name: name)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -29,4 +55,25 @@ public class PrecompiledNode: GraphNode {
|
||||||
case product
|
case product
|
||||||
case type
|
case type
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Adds a new dependency to the xcframework node.
|
||||||
|
/// - Parameter dependency: Dependency to be added.
|
||||||
|
public func add(dependency: Dependency) {
|
||||||
|
dependencies.append(dependency)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - CustomDebugStringConvertible
|
||||||
|
|
||||||
|
override public var debugDescription: String {
|
||||||
|
if dependencies.isEmpty {
|
||||||
|
return name
|
||||||
|
}
|
||||||
|
var dependenciesDescriptions: [String] = []
|
||||||
|
let uniqueDependencies = Set<Dependency>(dependencies)
|
||||||
|
uniqueDependencies.forEach { dependency in
|
||||||
|
dependenciesDescriptions.append(dependency.node.description)
|
||||||
|
}
|
||||||
|
|
||||||
|
return "\(name) --> [\(dependenciesDescriptions.joined(separator: ", "))]"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -51,6 +51,14 @@ public class SDKNode: GraphNode {
|
||||||
try! SDKNode(name: "XCTest.framework", platform: platform, status: status, source: .system)
|
try! SDKNode(name: "XCTest.framework", platform: platform, status: status, source: .system)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Creates an instace of SDKNode that represents the AppClip framework.
|
||||||
|
/// - Parameters:
|
||||||
|
/// - status: SDK status
|
||||||
|
/// - Returns: Initialized SDK node.
|
||||||
|
public static func appClip(status: SDKStatus) throws -> SDKNode {
|
||||||
|
try SDKNode(name: "AppClip.framework", platform: .iOS, status: status, source: .system)
|
||||||
|
}
|
||||||
|
|
||||||
static func path(name: String, platform: Platform, source _: SDKSource, type: Type) throws -> AbsolutePath {
|
static func path(name: String, platform: Platform, source _: SDKSource, type: Type) throws -> AbsolutePath {
|
||||||
let sdkRootPath: AbsolutePath
|
let sdkRootPath: AbsolutePath
|
||||||
if name == SDKNode.xctestFrameworkName {
|
if name == SDKNode.xctestFrameworkName {
|
||||||
|
|
|
@ -9,6 +9,9 @@ public class TargetNode: GraphNode {
|
||||||
public let target: Target
|
public let target: Target
|
||||||
public var dependencies: [GraphNode]
|
public var dependencies: [GraphNode]
|
||||||
|
|
||||||
|
/// When true it indicates that the target should be stripped from the graph when tree-shaking the project.
|
||||||
|
public var prune: Bool = false
|
||||||
|
|
||||||
enum CodingKeys: String, CodingKey {
|
enum CodingKeys: String, CodingKey {
|
||||||
case path
|
case path
|
||||||
case name
|
case name
|
||||||
|
@ -23,11 +26,13 @@ public class TargetNode: GraphNode {
|
||||||
|
|
||||||
public init(project: Project,
|
public init(project: Project,
|
||||||
target: Target,
|
target: Target,
|
||||||
dependencies: [GraphNode])
|
dependencies: [GraphNode],
|
||||||
|
prune: Bool = false)
|
||||||
{
|
{
|
||||||
self.project = project
|
self.project = project
|
||||||
self.target = target
|
self.target = target
|
||||||
self.dependencies = dependencies
|
self.dependencies = dependencies
|
||||||
|
self.prune = prune
|
||||||
super.init(path: project.path, name: target.name)
|
super.init(path: project.path, name: target.name)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,28 +3,6 @@ import TSCBasic
|
||||||
import TuistSupport
|
import TuistSupport
|
||||||
|
|
||||||
public class XCFrameworkNode: PrecompiledNode {
|
public class XCFrameworkNode: PrecompiledNode {
|
||||||
/// It represents a dependency of an .xcframework which can be either a framework, or another .xcframework.
|
|
||||||
public enum Dependency: Equatable {
|
|
||||||
case framework(FrameworkNode)
|
|
||||||
case xcframework(XCFrameworkNode)
|
|
||||||
|
|
||||||
/// Path to the dependency.
|
|
||||||
public var path: AbsolutePath {
|
|
||||||
switch self {
|
|
||||||
case let .framework(framework): return framework.path
|
|
||||||
case let .xcframework(xcframework): return xcframework.path
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the node that represents the dependency.
|
|
||||||
public var node: PrecompiledNode {
|
|
||||||
switch self {
|
|
||||||
case let .framework(framework): return framework
|
|
||||||
case let .xcframework(xcframework): return xcframework
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Coding keys.
|
/// Coding keys.
|
||||||
enum XCFrameworkNodeCodingKeys: String, CodingKey {
|
enum XCFrameworkNodeCodingKeys: String, CodingKey {
|
||||||
case linking
|
case linking
|
||||||
|
@ -43,9 +21,6 @@ public class XCFrameworkNode: PrecompiledNode {
|
||||||
/// Returns the type of linking
|
/// Returns the type of linking
|
||||||
public let linking: BinaryLinking
|
public let linking: BinaryLinking
|
||||||
|
|
||||||
/// List of other .xcframeworks this xcframework depends on.
|
|
||||||
public private(set) var dependencies: [Dependency]
|
|
||||||
|
|
||||||
/// Path to the binary.
|
/// Path to the binary.
|
||||||
override public var binaryPath: AbsolutePath { primaryBinaryPath }
|
override public var binaryPath: AbsolutePath { primaryBinaryPath }
|
||||||
|
|
||||||
|
@ -65,8 +40,7 @@ public class XCFrameworkNode: PrecompiledNode {
|
||||||
self.infoPlist = infoPlist
|
self.infoPlist = infoPlist
|
||||||
self.linking = linking
|
self.linking = linking
|
||||||
self.primaryBinaryPath = primaryBinaryPath
|
self.primaryBinaryPath = primaryBinaryPath
|
||||||
self.dependencies = dependencies
|
super.init(path: path, dependencies: dependencies)
|
||||||
super.init(path: path)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override public func encode(to encoder: Encoder) throws {
|
override public func encode(to encoder: Encoder) throws {
|
||||||
|
@ -77,10 +51,4 @@ public class XCFrameworkNode: PrecompiledNode {
|
||||||
try container.encode("xcframework", forKey: .type)
|
try container.encode("xcframework", forKey: .type)
|
||||||
try container.encode(infoPlist, forKey: .infoPlist)
|
try container.encode(infoPlist, forKey: .infoPlist)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Adds a new dependency to the xcframework node.
|
|
||||||
/// - Parameter dependency: Dependency to be added.
|
|
||||||
public func add(dependency: Dependency) {
|
|
||||||
dependencies.append(dependency)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -41,4 +41,10 @@ public protocol GraphTraversing {
|
||||||
/// - path: Path to the directory where the project that defines the target is located.
|
/// - path: Path to the directory where the project that defines the target is located.
|
||||||
/// - name: Name of the target.
|
/// - name: Name of the target.
|
||||||
func directStaticDependencies(path: AbsolutePath, name: String) -> [GraphDependencyReference]
|
func directStaticDependencies(path: AbsolutePath, name: String) -> [GraphDependencyReference]
|
||||||
|
|
||||||
|
/// Given a project directory and a target name, it returns an appClips dependency.
|
||||||
|
/// - Parameters:
|
||||||
|
/// - path: Path to the directory that contains the project.
|
||||||
|
/// - name: Target name.
|
||||||
|
func appClipsDependency(path: AbsolutePath, name: String) -> Target?
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@ import Foundation
|
||||||
import TSCBasic
|
import TSCBasic
|
||||||
import TuistSupport
|
import TuistSupport
|
||||||
|
|
||||||
protocol FrameworkMetadataProviding: PrecompiledMetadataProviding {
|
public protocol FrameworkMetadataProviding: PrecompiledMetadataProviding {
|
||||||
/// Given the path to a framework, it returns the path to its dSYMs if they exist
|
/// Given the path to a framework, it returns the path to its dSYMs if they exist
|
||||||
/// in the same framework directory.
|
/// in the same framework directory.
|
||||||
/// - Parameter frameworkPath: Path to the .framework directory.
|
/// - Parameter frameworkPath: Path to the .framework directory.
|
||||||
|
@ -18,14 +18,14 @@ protocol FrameworkMetadataProviding: PrecompiledMetadataProviding {
|
||||||
func product(frameworkPath: AbsolutePath) throws -> Product
|
func product(frameworkPath: AbsolutePath) throws -> Product
|
||||||
}
|
}
|
||||||
|
|
||||||
final class FrameworkMetadataProvider: PrecompiledMetadataProvider, FrameworkMetadataProviding {
|
public final class FrameworkMetadataProvider: PrecompiledMetadataProvider, FrameworkMetadataProviding {
|
||||||
func dsymPath(frameworkPath: AbsolutePath) -> AbsolutePath? {
|
public func dsymPath(frameworkPath: AbsolutePath) -> AbsolutePath? {
|
||||||
let path = AbsolutePath("\(frameworkPath.pathString).dSYM")
|
let path = AbsolutePath("\(frameworkPath.pathString).dSYM")
|
||||||
if FileHandler.shared.exists(path) { return path }
|
if FileHandler.shared.exists(path) { return path }
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func bcsymbolmapPaths(frameworkPath: AbsolutePath) throws -> [AbsolutePath] {
|
public func bcsymbolmapPaths(frameworkPath: AbsolutePath) throws -> [AbsolutePath] {
|
||||||
let binaryPath = FrameworkNode.binaryPath(frameworkPath: frameworkPath)
|
let binaryPath = FrameworkNode.binaryPath(frameworkPath: frameworkPath)
|
||||||
let uuids = try self.uuids(binaryPath: binaryPath)
|
let uuids = try self.uuids(binaryPath: binaryPath)
|
||||||
return uuids
|
return uuids
|
||||||
|
@ -34,7 +34,7 @@ final class FrameworkMetadataProvider: PrecompiledMetadataProvider, FrameworkMet
|
||||||
.sorted()
|
.sorted()
|
||||||
}
|
}
|
||||||
|
|
||||||
func product(frameworkPath: AbsolutePath) throws -> Product {
|
public func product(frameworkPath: AbsolutePath) throws -> Product {
|
||||||
let binaryPath = FrameworkNode.binaryPath(frameworkPath: frameworkPath)
|
let binaryPath = FrameworkNode.binaryPath(frameworkPath: frameworkPath)
|
||||||
switch try linking(binaryPath: binaryPath) {
|
switch try linking(binaryPath: binaryPath) {
|
||||||
case .dynamic:
|
case .dynamic:
|
||||||
|
|
|
@ -31,7 +31,7 @@ enum PrecompiledMetadataProviderError: FatalError, Equatable {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protocol PrecompiledMetadataProviding {
|
public protocol PrecompiledMetadataProviding {
|
||||||
/// It returns the supported architectures of the binary at the given path.
|
/// It returns the supported architectures of the binary at the given path.
|
||||||
/// - Parameter binaryPath: Binary path.
|
/// - Parameter binaryPath: Binary path.
|
||||||
func architectures(binaryPath: AbsolutePath) throws -> [BinaryArchitecture]
|
func architectures(binaryPath: AbsolutePath) throws -> [BinaryArchitecture]
|
||||||
|
@ -46,10 +46,10 @@ protocol PrecompiledMetadataProviding {
|
||||||
func uuids(binaryPath: AbsolutePath) throws -> Set<UUID>
|
func uuids(binaryPath: AbsolutePath) throws -> Set<UUID>
|
||||||
}
|
}
|
||||||
|
|
||||||
class PrecompiledMetadataProvider: PrecompiledMetadataProviding {
|
public class PrecompiledMetadataProvider: PrecompiledMetadataProviding {
|
||||||
public init() {}
|
public init() {}
|
||||||
|
|
||||||
func architectures(binaryPath: AbsolutePath) throws -> [BinaryArchitecture] {
|
public func architectures(binaryPath: AbsolutePath) throws -> [BinaryArchitecture] {
|
||||||
let result = try System.shared.capture("/usr/bin/lipo", "-info", binaryPath.pathString).spm_chuzzle() ?? ""
|
let result = try System.shared.capture("/usr/bin/lipo", "-info", binaryPath.pathString).spm_chuzzle() ?? ""
|
||||||
let regexes = [
|
let regexes = [
|
||||||
// Non-fat file: path is architecture: x86_64
|
// Non-fat file: path is architecture: x86_64
|
||||||
|
@ -70,12 +70,12 @@ class PrecompiledMetadataProvider: PrecompiledMetadataProviding {
|
||||||
return architectures
|
return architectures
|
||||||
}
|
}
|
||||||
|
|
||||||
func linking(binaryPath: AbsolutePath) throws -> BinaryLinking {
|
public func linking(binaryPath: AbsolutePath) throws -> BinaryLinking {
|
||||||
let result = try System.shared.capture("/usr/bin/file", binaryPath.pathString).spm_chuzzle() ?? ""
|
let result = try System.shared.capture("/usr/bin/file", binaryPath.pathString).spm_chuzzle() ?? ""
|
||||||
return result.contains("dynamically linked") ? .dynamic : .static
|
return result.contains("dynamically linked") ? .dynamic : .static
|
||||||
}
|
}
|
||||||
|
|
||||||
func uuids(binaryPath: AbsolutePath) throws -> Set<UUID> {
|
public func uuids(binaryPath: AbsolutePath) throws -> Set<UUID> {
|
||||||
let output = try System.shared.capture(["/usr/bin/xcrun", "dwarfdump", "--uuid", binaryPath.pathString])
|
let output = try System.shared.capture(["/usr/bin/xcrun", "dwarfdump", "--uuid", binaryPath.pathString])
|
||||||
// UUIDs are letters, decimals, or hyphens.
|
// UUIDs are letters, decimals, or hyphens.
|
||||||
var uuidCharacterSet = CharacterSet()
|
var uuidCharacterSet = CharacterSet()
|
||||||
|
|
|
@ -13,3 +13,11 @@ public enum BinaryArchitecture: String, Codable {
|
||||||
public enum BinaryLinking: String, Codable {
|
public enum BinaryLinking: String, Codable {
|
||||||
case `static`, dynamic
|
case `static`, dynamic
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public extension Sequence where Element == BinaryArchitecture {
|
||||||
|
/// Returns true if all the architectures are only for simulator.
|
||||||
|
var onlySimulator: Bool {
|
||||||
|
let simulatorArchitectures: [BinaryArchitecture] = [.x8664, .i386]
|
||||||
|
return allSatisfy { simulatorArchitectures.contains($0) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -10,8 +10,10 @@ public struct Config: Equatable, Hashable {
|
||||||
public enum GenerationOption: Hashable, Equatable {
|
public enum GenerationOption: Hashable, Equatable {
|
||||||
case xcodeProjectName(String)
|
case xcodeProjectName(String)
|
||||||
case organizationName(String)
|
case organizationName(String)
|
||||||
|
case developmentRegion(String)
|
||||||
case disableAutogeneratedSchemes
|
case disableAutogeneratedSchemes
|
||||||
case disableSynthesizedResourceAccessors
|
case disableSynthesizedResourceAccessors
|
||||||
|
case disableShowEnvironmentVarsInScriptPhases
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Generation options.
|
/// Generation options.
|
||||||
|
@ -23,9 +25,12 @@ public struct Config: Equatable, Hashable {
|
||||||
/// Cloud configuration.
|
/// Cloud configuration.
|
||||||
public let cloud: Cloud?
|
public let cloud: Cloud?
|
||||||
|
|
||||||
|
/// The path of the config file.
|
||||||
|
public let path: AbsolutePath?
|
||||||
|
|
||||||
/// Returns the default Tuist configuration.
|
/// Returns the default Tuist configuration.
|
||||||
public static var `default`: Config {
|
public static var `default`: Config {
|
||||||
Config(compatibleXcodeVersions: .all, cloud: nil, generationOptions: [])
|
Config(compatibleXcodeVersions: .all, cloud: nil, generationOptions: [], path: nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Initializes the tuist cofiguration.
|
/// Initializes the tuist cofiguration.
|
||||||
|
@ -34,13 +39,16 @@ public struct Config: Equatable, Hashable {
|
||||||
/// - compatibleXcodeVersions: List of Xcode versions the project or set of projects is compatible with.
|
/// - compatibleXcodeVersions: List of Xcode versions the project or set of projects is compatible with.
|
||||||
/// - cloud: Cloud configuration.
|
/// - cloud: Cloud configuration.
|
||||||
/// - generationOptions: Generation options.
|
/// - generationOptions: Generation options.
|
||||||
|
/// - path: The path of the config file.
|
||||||
public init(compatibleXcodeVersions: CompatibleXcodeVersions,
|
public init(compatibleXcodeVersions: CompatibleXcodeVersions,
|
||||||
cloud: Cloud?,
|
cloud: Cloud?,
|
||||||
generationOptions: [GenerationOption])
|
generationOptions: [GenerationOption],
|
||||||
|
path: AbsolutePath?)
|
||||||
{
|
{
|
||||||
self.compatibleXcodeVersions = compatibleXcodeVersions
|
self.compatibleXcodeVersions = compatibleXcodeVersions
|
||||||
self.cloud = cloud
|
self.cloud = cloud
|
||||||
self.generationOptions = generationOptions
|
self.generationOptions = generationOptions
|
||||||
|
self.path = path
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Hashable
|
// MARK: - Hashable
|
||||||
|
|
|
@ -7,15 +7,18 @@ public struct ExecutionAction: Equatable {
|
||||||
public let title: String
|
public let title: String
|
||||||
public let scriptText: String
|
public let scriptText: String
|
||||||
public let target: TargetReference?
|
public let target: TargetReference?
|
||||||
|
public let showEnvVarsInLog: Bool
|
||||||
|
|
||||||
// MARK: - Init
|
// MARK: - Init
|
||||||
|
|
||||||
public init(title: String,
|
public init(title: String,
|
||||||
scriptText: String,
|
scriptText: String,
|
||||||
target: TargetReference?)
|
target: TargetReference?,
|
||||||
|
showEnvVarsInLog: Bool = true)
|
||||||
{
|
{
|
||||||
self.title = title
|
self.title = title
|
||||||
self.scriptText = scriptText
|
self.scriptText = scriptText
|
||||||
self.target = target
|
self.target = target
|
||||||
|
self.showEnvVarsInLog = showEnvVarsInLog
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,6 +19,7 @@ public enum Product: String, CustomStringConvertible, CaseIterable, Encodable {
|
||||||
// case messagesApplication = "messages_application"
|
// case messagesApplication = "messages_application"
|
||||||
case messagesExtension = "messages_extension"
|
case messagesExtension = "messages_extension"
|
||||||
case stickerPackExtension = "sticker_pack_extension"
|
case stickerPackExtension = "sticker_pack_extension"
|
||||||
|
case appClip
|
||||||
|
|
||||||
public var caseValue: String {
|
public var caseValue: String {
|
||||||
switch self {
|
switch self {
|
||||||
|
@ -56,6 +57,8 @@ public enum Product: String, CustomStringConvertible, CaseIterable, Encodable {
|
||||||
return "messagesExtension"
|
return "messagesExtension"
|
||||||
case .stickerPackExtension:
|
case .stickerPackExtension:
|
||||||
return "stickerPackExtension"
|
return "stickerPackExtension"
|
||||||
|
case .appClip:
|
||||||
|
return "appClip"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -95,13 +98,15 @@ public enum Product: String, CustomStringConvertible, CaseIterable, Encodable {
|
||||||
return "iMessage extension"
|
return "iMessage extension"
|
||||||
case .stickerPackExtension:
|
case .stickerPackExtension:
|
||||||
return "sticker pack extension"
|
return "sticker pack extension"
|
||||||
|
case .appClip:
|
||||||
|
return "appClip"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns true if the target can be ran.
|
/// Returns true if the target can be ran.
|
||||||
public var runnable: Bool {
|
public var runnable: Bool {
|
||||||
switch self {
|
switch self {
|
||||||
case .app:
|
case .app, .appClip:
|
||||||
return true
|
return true
|
||||||
default:
|
default:
|
||||||
return false
|
return false
|
||||||
|
@ -126,6 +131,7 @@ public enum Product: String, CustomStringConvertible, CaseIterable, Encodable {
|
||||||
base.append(.stickerPackExtension)
|
base.append(.stickerPackExtension)
|
||||||
// base.append(.messagesApplication)
|
// base.append(.messagesApplication)
|
||||||
base.append(.messagesExtension)
|
base.append(.messagesExtension)
|
||||||
|
base.append(.appClip)
|
||||||
}
|
}
|
||||||
|
|
||||||
if platform == .tvOS {
|
if platform == .tvOS {
|
||||||
|
@ -195,6 +201,12 @@ public enum Product: String, CustomStringConvertible, CaseIterable, Encodable {
|
||||||
return .messagesExtension
|
return .messagesExtension
|
||||||
case .stickerPackExtension:
|
case .stickerPackExtension:
|
||||||
return .stickerPack
|
return .stickerPack
|
||||||
|
case .appClip:
|
||||||
|
return .onDemandInstallCapableApplication
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public func canHostTests() -> Bool {
|
||||||
|
[.app, .appClip].contains(self)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,6 +9,7 @@ public struct Project: Hashable, Equatable, CustomStringConvertible, CustomDebug
|
||||||
lhs.xcodeProjPath == rhs.xcodeProjPath &&
|
lhs.xcodeProjPath == rhs.xcodeProjPath &&
|
||||||
lhs.name == rhs.name &&
|
lhs.name == rhs.name &&
|
||||||
lhs.organizationName == rhs.organizationName &&
|
lhs.organizationName == rhs.organizationName &&
|
||||||
|
lhs.developmentRegion == rhs.developmentRegion &&
|
||||||
lhs.targets == rhs.targets &&
|
lhs.targets == rhs.targets &&
|
||||||
lhs.packages == rhs.packages &&
|
lhs.packages == rhs.packages &&
|
||||||
lhs.schemes == rhs.schemes &&
|
lhs.schemes == rhs.schemes &&
|
||||||
|
@ -34,6 +35,9 @@ public struct Project: Hashable, Equatable, CustomStringConvertible, CustomDebug
|
||||||
/// Organization name.
|
/// Organization name.
|
||||||
public var organizationName: String?
|
public var organizationName: String?
|
||||||
|
|
||||||
|
/// Development region code e.g. `en`.
|
||||||
|
public var developmentRegion: String?
|
||||||
|
|
||||||
/// Project targets.
|
/// Project targets.
|
||||||
public var targets: [Target]
|
public var targets: [Target]
|
||||||
|
|
||||||
|
@ -62,6 +66,7 @@ public struct Project: Hashable, Equatable, CustomStringConvertible, CustomDebug
|
||||||
/// - xcodeProjPath: Path to the Xcode project that will be generated.
|
/// - xcodeProjPath: Path to the Xcode project that will be generated.
|
||||||
/// - name: Project name.
|
/// - name: Project name.
|
||||||
/// - organizationName: Organization name.
|
/// - organizationName: Organization name.
|
||||||
|
/// - developmentRegion: Development region.
|
||||||
/// - settings: The settings to apply at the project level
|
/// - settings: The settings to apply at the project level
|
||||||
/// - filesGroup: The root group to place project files within
|
/// - filesGroup: The root group to place project files within
|
||||||
/// - targets: The project targets
|
/// - targets: The project targets
|
||||||
|
@ -72,6 +77,7 @@ public struct Project: Hashable, Equatable, CustomStringConvertible, CustomDebug
|
||||||
xcodeProjPath: AbsolutePath,
|
xcodeProjPath: AbsolutePath,
|
||||||
name: String,
|
name: String,
|
||||||
organizationName: String?,
|
organizationName: String?,
|
||||||
|
developmentRegion: String?,
|
||||||
settings: Settings,
|
settings: Settings,
|
||||||
filesGroup: ProjectGroup,
|
filesGroup: ProjectGroup,
|
||||||
targets: [Target],
|
targets: [Target],
|
||||||
|
@ -84,6 +90,7 @@ public struct Project: Hashable, Equatable, CustomStringConvertible, CustomDebug
|
||||||
self.xcodeProjPath = xcodeProjPath
|
self.xcodeProjPath = xcodeProjPath
|
||||||
self.name = name
|
self.name = name
|
||||||
self.organizationName = organizationName
|
self.organizationName = organizationName
|
||||||
|
self.developmentRegion = developmentRegion
|
||||||
self.targets = targets
|
self.targets = targets
|
||||||
self.packages = packages
|
self.packages = packages
|
||||||
self.schemes = schemes
|
self.schemes = schemes
|
||||||
|
@ -155,6 +162,7 @@ public struct Project: Hashable, Equatable, CustomStringConvertible, CustomDebug
|
||||||
xcodeProjPath: xcodeProjPath,
|
xcodeProjPath: xcodeProjPath,
|
||||||
name: name,
|
name: name,
|
||||||
organizationName: organizationName,
|
organizationName: organizationName,
|
||||||
|
developmentRegion: developmentRegion,
|
||||||
settings: settings,
|
settings: settings,
|
||||||
filesGroup: filesGroup,
|
filesGroup: filesGroup,
|
||||||
targets: targets,
|
targets: targets,
|
||||||
|
@ -171,6 +179,7 @@ public struct Project: Hashable, Equatable, CustomStringConvertible, CustomDebug
|
||||||
xcodeProjPath: xcodeProjPath,
|
xcodeProjPath: xcodeProjPath,
|
||||||
name: name,
|
name: name,
|
||||||
organizationName: organizationName,
|
organizationName: organizationName,
|
||||||
|
developmentRegion: developmentRegion,
|
||||||
settings: settings,
|
settings: settings,
|
||||||
filesGroup: filesGroup,
|
filesGroup: filesGroup,
|
||||||
targets: targets,
|
targets: targets,
|
||||||
|
|
|
@ -47,6 +47,7 @@ public struct Target: Equatable, Hashable, Comparable {
|
||||||
public var environment: [String: String]
|
public var environment: [String: String]
|
||||||
public var launchArguments: [String: Bool]
|
public var launchArguments: [String: Bool]
|
||||||
public var filesGroup: ProjectGroup
|
public var filesGroup: ProjectGroup
|
||||||
|
public var scripts: [TargetScript]
|
||||||
|
|
||||||
// MARK: - Init
|
// MARK: - Init
|
||||||
|
|
||||||
|
@ -67,7 +68,8 @@ public struct Target: Equatable, Hashable, Comparable {
|
||||||
environment: [String: String] = [:],
|
environment: [String: String] = [:],
|
||||||
launchArguments: [String: Bool] = [:],
|
launchArguments: [String: Bool] = [:],
|
||||||
filesGroup: ProjectGroup,
|
filesGroup: ProjectGroup,
|
||||||
dependencies: [Dependency] = [])
|
dependencies: [Dependency] = [],
|
||||||
|
scripts: [TargetScript] = [])
|
||||||
{
|
{
|
||||||
self.name = name
|
self.name = name
|
||||||
self.product = product
|
self.product = product
|
||||||
|
@ -87,6 +89,7 @@ public struct Target: Equatable, Hashable, Comparable {
|
||||||
self.launchArguments = launchArguments
|
self.launchArguments = launchArguments
|
||||||
self.filesGroup = filesGroup
|
self.filesGroup = filesGroup
|
||||||
self.dependencies = dependencies
|
self.dependencies = dependencies
|
||||||
|
self.scripts = scripts
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Target can be included in the link phase of other targets
|
/// Target can be included in the link phase of other targets
|
||||||
|
@ -116,6 +119,17 @@ public struct Target: Equatable, Hashable, Comparable {
|
||||||
].contains(product)
|
].contains(product)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// It returns the name of the variable that should be used to create an empty file
|
||||||
|
/// in the $BUILT_PRODUCTS_DIR directory that is used after builds to reliably locate the
|
||||||
|
/// directories where the products have been exported into.
|
||||||
|
public var targetLocatorBuildPhaseVariable: String {
|
||||||
|
let upperCasedSnakeCasedProductName = productName
|
||||||
|
.camelCaseToSnakeCase()
|
||||||
|
.components(separatedBy: .whitespaces).joined(separator: "_")
|
||||||
|
.uppercased()
|
||||||
|
return "\(upperCasedSnakeCasedProductName)_LOCATE_HASH"
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns the product name including the extension.
|
/// Returns the product name including the extension.
|
||||||
public var productNameWithExtension: String {
|
public var productNameWithExtension: String {
|
||||||
switch product {
|
switch product {
|
||||||
|
@ -126,6 +140,16 @@ public struct Target: Equatable, Hashable, Comparable {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns true if the target supports having a headers build phase..
|
||||||
|
public var shouldIncludeHeadersBuildPhase: Bool {
|
||||||
|
switch product {
|
||||||
|
case .framework, .staticFramework, .staticLibrary, .dynamicLibrary:
|
||||||
|
return true
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns true if the target supports having sources.
|
/// Returns true if the target supports having sources.
|
||||||
public var supportsSources: Bool {
|
public var supportsSources: Bool {
|
||||||
switch (platform, product) {
|
switch (platform, product) {
|
||||||
|
@ -146,6 +170,14 @@ public struct Target: Equatable, Hashable, Comparable {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns true if the target is an AppClip
|
||||||
|
public var isAppClip: Bool {
|
||||||
|
if case .appClip = product {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns true if the file at the given path is a resource.
|
/// Returns true if the file at the given path is a resource.
|
||||||
/// - Parameter path: Path to the file to be checked.
|
/// - Parameter path: Path to the file to be checked.
|
||||||
public static func isResource(path: AbsolutePath) -> Bool {
|
public static func isResource(path: AbsolutePath) -> Bool {
|
||||||
|
@ -263,8 +295,8 @@ extension Sequence where Element == Target {
|
||||||
filter { $0.product.testsBundle }
|
filter { $0.product.testsBundle }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Filters and returns only the targets that are apps.
|
/// Filters and returns only the targets that are apps and app clips.
|
||||||
var apps: [Target] {
|
var apps: [Target] {
|
||||||
filter { $0.product == .app }
|
filter { $0.product == .app || $0.product == .appClip }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -40,6 +40,12 @@ public struct TargetAction: Equatable {
|
||||||
/// List of output filelist paths
|
/// List of output filelist paths
|
||||||
public let outputFileListPaths: [AbsolutePath]
|
public let outputFileListPaths: [AbsolutePath]
|
||||||
|
|
||||||
|
/// Show environment variables in the logs
|
||||||
|
public var showEnvVarsInLog: Bool
|
||||||
|
|
||||||
|
/// Whether to skip running this script in incremental builds, if nothing has changed
|
||||||
|
public let basedOnDependencyAnalysis: Bool?
|
||||||
|
|
||||||
/// Initializes a new target action with its attributes.
|
/// Initializes a new target action with its attributes.
|
||||||
///
|
///
|
||||||
/// - Parameters:
|
/// - Parameters:
|
||||||
|
@ -52,6 +58,8 @@ public struct TargetAction: Equatable {
|
||||||
/// - inputFileListPaths: List of input filelist paths
|
/// - inputFileListPaths: List of input filelist paths
|
||||||
/// - outputPaths: List of output file paths
|
/// - outputPaths: List of output file paths
|
||||||
/// - outputFileListPaths: List of output filelist paths
|
/// - outputFileListPaths: List of output filelist paths
|
||||||
|
/// - showEnvVarsInLog: Show environment variables in the logs
|
||||||
|
/// - basedOnDependencyAnalysis: Whether to skip running this script in incremental builds
|
||||||
public init(name: String,
|
public init(name: String,
|
||||||
order: Order,
|
order: Order,
|
||||||
tool: String? = nil,
|
tool: String? = nil,
|
||||||
|
@ -60,7 +68,9 @@ public struct TargetAction: Equatable {
|
||||||
inputPaths: [AbsolutePath] = [],
|
inputPaths: [AbsolutePath] = [],
|
||||||
inputFileListPaths: [AbsolutePath] = [],
|
inputFileListPaths: [AbsolutePath] = [],
|
||||||
outputPaths: [AbsolutePath] = [],
|
outputPaths: [AbsolutePath] = [],
|
||||||
outputFileListPaths: [AbsolutePath] = [])
|
outputFileListPaths: [AbsolutePath] = [],
|
||||||
|
showEnvVarsInLog: Bool = true,
|
||||||
|
basedOnDependencyAnalysis: Bool? = nil)
|
||||||
{
|
{
|
||||||
self.name = name
|
self.name = name
|
||||||
self.order = order
|
self.order = order
|
||||||
|
@ -71,6 +81,8 @@ public struct TargetAction: Equatable {
|
||||||
self.inputFileListPaths = inputFileListPaths
|
self.inputFileListPaths = inputFileListPaths
|
||||||
self.outputPaths = outputPaths
|
self.outputPaths = outputPaths
|
||||||
self.outputFileListPaths = outputFileListPaths
|
self.outputFileListPaths = outputFileListPaths
|
||||||
|
self.showEnvVarsInLog = showEnvVarsInLog
|
||||||
|
self.basedOnDependencyAnalysis = basedOnDependencyAnalysis
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the shell script that should be used in the target build phase.
|
/// Returns the shell script that should be used in the target build phase.
|
||||||
|
|
|
@ -0,0 +1,34 @@
|
||||||
|
import Foundation
|
||||||
|
import TSCBasic
|
||||||
|
import TuistSupport
|
||||||
|
|
||||||
|
/// It represents a target build phase.
|
||||||
|
public struct TargetScript: Equatable {
|
||||||
|
/// The name of the build phase.
|
||||||
|
public let name: String
|
||||||
|
|
||||||
|
/// Script.
|
||||||
|
public let script: String
|
||||||
|
|
||||||
|
/// Whether we want the build phase to show the environment variables in the logs.
|
||||||
|
public let showEnvVarsInLog: Bool
|
||||||
|
|
||||||
|
/// Whether the script should be hashed for caching purposes.
|
||||||
|
public let hashable: Bool
|
||||||
|
|
||||||
|
/// Initializes the target script.
|
||||||
|
/// - Parameter name: The name of the build phase.
|
||||||
|
/// - Parameter script: Script.
|
||||||
|
/// - Parameter showEnvVarsInLog: Whether we want the build phase to show the environment variables in the logs.
|
||||||
|
/// - Parameter hashable: Whether the script should be hashed for caching purposes.
|
||||||
|
public init(name: String,
|
||||||
|
script: String,
|
||||||
|
showEnvVarsInLog: Bool,
|
||||||
|
hashable: Bool)
|
||||||
|
{
|
||||||
|
self.name = name
|
||||||
|
self.script = script
|
||||||
|
self.showEnvVarsInLog = showEnvVarsInLog
|
||||||
|
self.hashable = hashable
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,7 +1,7 @@
|
||||||
import Foundation
|
import Foundation
|
||||||
import TSCBasic
|
import TSCBasic
|
||||||
|
|
||||||
public struct TestableTarget: Equatable {
|
public struct TestableTarget: Equatable, Hashable {
|
||||||
public let target: TargetReference
|
public let target: TargetReference
|
||||||
public let isSkipped: Bool
|
public let isSkipped: Bool
|
||||||
public let isParallelizable: Bool
|
public let isParallelizable: Bool
|
||||||
|
|
|
@ -5,11 +5,11 @@ import TuistSupport
|
||||||
public struct Workspace: Equatable {
|
public struct Workspace: Equatable {
|
||||||
// MARK: - Attributes
|
// MARK: - Attributes
|
||||||
|
|
||||||
public let path: AbsolutePath
|
public var path: AbsolutePath
|
||||||
public let name: String
|
public var name: String
|
||||||
public let projects: [AbsolutePath]
|
public var projects: [AbsolutePath]
|
||||||
public let schemes: [Scheme]
|
public var schemes: [Scheme]
|
||||||
public let additionalFiles: [FileElement]
|
public var additionalFiles: [FileElement]
|
||||||
|
|
||||||
// MARK: - Init
|
// MARK: - Init
|
||||||
|
|
||||||
|
@ -23,6 +23,12 @@ public struct Workspace: Equatable {
|
||||||
}
|
}
|
||||||
|
|
||||||
extension Workspace {
|
extension Workspace {
|
||||||
|
public func with(name: String) -> Workspace {
|
||||||
|
var copy = self
|
||||||
|
copy.name = name
|
||||||
|
return copy
|
||||||
|
}
|
||||||
|
|
||||||
public func adding(files: [AbsolutePath]) -> Workspace {
|
public func adding(files: [AbsolutePath]) -> Workspace {
|
||||||
Workspace(path: path,
|
Workspace(path: path,
|
||||||
name: name,
|
name: name,
|
||||||
|
|
|
@ -28,17 +28,17 @@ public protocol FrameworkNodeLoading {
|
||||||
func load(path: AbsolutePath) throws -> FrameworkNode
|
func load(path: AbsolutePath) throws -> FrameworkNode
|
||||||
}
|
}
|
||||||
|
|
||||||
final class FrameworkNodeLoader: FrameworkNodeLoading {
|
public final class FrameworkNodeLoader: FrameworkNodeLoading {
|
||||||
/// Framework metadata provider.
|
/// Framework metadata provider.
|
||||||
fileprivate let frameworkMetadataProvider: FrameworkMetadataProviding
|
fileprivate let frameworkMetadataProvider: FrameworkMetadataProviding
|
||||||
|
|
||||||
/// Initializes the loader with its attributes.
|
/// Initializes the loader with its attributes.
|
||||||
/// - Parameter frameworkMetadataProvider: Framework metadata provider.
|
/// - Parameter frameworkMetadataProvider: Framework metadata provider.
|
||||||
init(frameworkMetadataProvider: FrameworkMetadataProviding = FrameworkMetadataProvider()) {
|
public init(frameworkMetadataProvider: FrameworkMetadataProviding = FrameworkMetadataProvider()) {
|
||||||
self.frameworkMetadataProvider = frameworkMetadataProvider
|
self.frameworkMetadataProvider = frameworkMetadataProvider
|
||||||
}
|
}
|
||||||
|
|
||||||
func load(path: AbsolutePath) throws -> FrameworkNode {
|
public func load(path: AbsolutePath) throws -> FrameworkNode {
|
||||||
guard FileHandler.shared.exists(path) else {
|
guard FileHandler.shared.exists(path) else {
|
||||||
throw FrameworkNodeLoaderError.frameworkNotFound(path)
|
throw FrameworkNodeLoaderError.frameworkNotFound(path)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,38 +1,62 @@
|
||||||
import Foundation
|
import Foundation
|
||||||
import RxSwift
|
import RxSwift
|
||||||
|
import struct TSCUtility.Version
|
||||||
import TuistSupport
|
import TuistSupport
|
||||||
|
|
||||||
protocol SimulatorControlling {
|
public protocol SimulatorControlling {
|
||||||
/// Returns the list of simulator devices that are available in the system.
|
/// Returns the list of simulator devices that are available in the system.
|
||||||
func devices() -> Single<[SimulatorDevice]>
|
func devices() -> Single<[SimulatorDevice]>
|
||||||
|
|
||||||
/// Returns the list of simulator runtimes that are available in the system.
|
/// Returns the list of simulator runtimes that are available in the system.
|
||||||
func runtimes() -> Single<[SimulatorRuntime]>
|
func runtimes() -> Single<[SimulatorRuntime]>
|
||||||
|
|
||||||
/// Returns the list of simulator devices and runtimes.
|
/// - Parameters:
|
||||||
|
/// - platform: Optionally filter by platform
|
||||||
|
/// - deviceName: Optionally filter by device name
|
||||||
|
/// - Returns: the list of simulator devices and runtimes.
|
||||||
func devicesAndRuntimes() -> Single<[SimulatorDeviceAndRuntime]>
|
func devicesAndRuntimes() -> Single<[SimulatorDeviceAndRuntime]>
|
||||||
|
|
||||||
|
/// Finds first available device defined by given parameters
|
||||||
|
/// - Parameters:
|
||||||
|
/// - platform: Given platform
|
||||||
|
/// - version: Specific version, ignored if nil
|
||||||
|
/// - minVersion: Minimum version of the OS
|
||||||
|
/// - deviceName: Specific device name (eg. iPhone X)
|
||||||
|
func findAvailableDevice(
|
||||||
|
platform: Platform,
|
||||||
|
version: Version?,
|
||||||
|
minVersion: Version?,
|
||||||
|
deviceName: String?
|
||||||
|
) -> Single<SimulatorDevice>
|
||||||
}
|
}
|
||||||
|
|
||||||
enum SimulatorControllerError: FatalError {
|
public enum SimulatorControllerError: FatalError {
|
||||||
case simctlError(String)
|
case simctlError(String)
|
||||||
|
case deviceNotFound(Platform, Version?, String?, [SimulatorDeviceAndRuntime])
|
||||||
|
|
||||||
var type: ErrorType {
|
public var type: ErrorType {
|
||||||
switch self {
|
switch self {
|
||||||
case .simctlError: return .abort
|
case .simctlError, .deviceNotFound:
|
||||||
|
return .abort
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var description: String {
|
public var description: String {
|
||||||
switch self {
|
switch self {
|
||||||
case let .simctlError(output): return output
|
case let .simctlError(output):
|
||||||
|
return output
|
||||||
|
case let .deviceNotFound(platform, version, deviceName, devices):
|
||||||
|
return "Could not find a suitable device for \(platform.caseValue)\(version.map { " \($0)" } ?? "")\(deviceName.map { ", device name \($0)" } ?? ""). Did find \(devices.map { "\($0.device.name) (\($0.runtime.description))" }.joined(separator: ", "))"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
final class SimulatorController: SimulatorControlling {
|
public final class SimulatorController: SimulatorControlling {
|
||||||
private let jsonDecoder: JSONDecoder = JSONDecoder()
|
private let jsonDecoder = JSONDecoder()
|
||||||
|
|
||||||
func devices() -> Single<[SimulatorDevice]> {
|
public init() {}
|
||||||
|
|
||||||
|
public func devices() -> Single<[SimulatorDevice]> {
|
||||||
System.shared.observable(["/usr/bin/xcrun", "simctl", "list", "devices", "--json"])
|
System.shared.observable(["/usr/bin/xcrun", "simctl", "list", "devices", "--json"])
|
||||||
.mapToString()
|
.mapToString()
|
||||||
.collectOutput()
|
.collectOutput()
|
||||||
|
@ -63,9 +87,8 @@ final class SimulatorController: SimulatorControlling {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func runtimes() -> Single<[SimulatorRuntime]> {
|
public func runtimes() -> Single<[SimulatorRuntime]> {
|
||||||
System.shared.observable(["/usr/bin/xcrun", "simctl", "list", "runtimes", "--json"])
|
System.shared.observable(["/usr/bin/xcrun", "simctl", "list", "runtimes", "--json"])
|
||||||
.debug()
|
|
||||||
.mapToString()
|
.mapToString()
|
||||||
.collectOutput()
|
.collectOutput()
|
||||||
.asSingle()
|
.asSingle()
|
||||||
|
@ -88,7 +111,7 @@ final class SimulatorController: SimulatorControlling {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func devicesAndRuntimes() -> Single<[SimulatorDeviceAndRuntime]> {
|
public func devicesAndRuntimes() -> Single<[SimulatorDeviceAndRuntime]> {
|
||||||
runtimes()
|
runtimes()
|
||||||
.flatMap { (runtimes) -> Single<([SimulatorDevice], [SimulatorRuntime])> in
|
.flatMap { (runtimes) -> Single<([SimulatorDevice], [SimulatorRuntime])> in
|
||||||
self.devices().map { ($0, runtimes) }
|
self.devices().map { ($0, runtimes) }
|
||||||
|
@ -100,4 +123,36 @@ final class SimulatorController: SimulatorControlling {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public func findAvailableDevice(
|
||||||
|
platform: Platform,
|
||||||
|
version: Version?,
|
||||||
|
minVersion: Version?,
|
||||||
|
deviceName: String?
|
||||||
|
) -> Single<SimulatorDevice> {
|
||||||
|
devicesAndRuntimes()
|
||||||
|
.flatMap { devicesAndRuntimes in
|
||||||
|
let availableDevices = devicesAndRuntimes
|
||||||
|
.filter { simulatorDeviceAndRuntime in
|
||||||
|
let nameComponents = simulatorDeviceAndRuntime.runtime.name.components(separatedBy: " ")
|
||||||
|
guard nameComponents.first == platform.caseValue else { return false }
|
||||||
|
let deviceVersion = nameComponents.last?.version()
|
||||||
|
if let version = version {
|
||||||
|
guard deviceVersion == version else { return false }
|
||||||
|
} else if let minVersion = minVersion, let deviceVersion = deviceVersion {
|
||||||
|
guard deviceVersion >= minVersion else { return false }
|
||||||
|
}
|
||||||
|
if let deviceName = deviceName {
|
||||||
|
guard simulatorDeviceAndRuntime.device.name == deviceName else { return false }
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
.map(\.device)
|
||||||
|
guard
|
||||||
|
let device = availableDevices.first(where: { !$0.isShutdown }) ?? availableDevices.first
|
||||||
|
else { return .error(SimulatorControllerError.deviceNotFound(platform, version, deviceName, devicesAndRuntimes)) }
|
||||||
|
|
||||||
|
return .just(device)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -2,44 +2,45 @@ import Foundation
|
||||||
import TSCBasic
|
import TSCBasic
|
||||||
|
|
||||||
/// It represents a simulator device. Devices are obtained using Xcode's CLI simctl
|
/// It represents a simulator device. Devices are obtained using Xcode's CLI simctl
|
||||||
struct SimulatorDevice: Decodable, Hashable, CustomStringConvertible {
|
public struct SimulatorDevice: Decodable, Hashable, CustomStringConvertible {
|
||||||
/// Device data path.
|
/// Device data path.
|
||||||
let dataPath: AbsolutePath
|
public let dataPath: AbsolutePath
|
||||||
|
|
||||||
/// Device log path.
|
/// Device log path.
|
||||||
let logPath: AbsolutePath
|
public let logPath: AbsolutePath
|
||||||
|
|
||||||
/// Device unique identifier (3A8C9673-C1FD-4E33-8EFA-AEEBF43161CC)
|
/// Device unique identifier (3A8C9673-C1FD-4E33-8EFA-AEEBF43161CC)
|
||||||
let udid: String
|
public let udid: String
|
||||||
|
|
||||||
/// Whether the device is available or not.
|
/// Whether the device is available or not.
|
||||||
let isAvailable: Bool
|
public let isAvailable: Bool
|
||||||
|
|
||||||
/// Device type identifier (e.g. com.apple.CoreSimulator.SimDeviceType.iPad-Air--3rd-generation-)
|
/// Device type identifier (e.g. com.apple.CoreSimulator.SimDeviceType.iPad-Air--3rd-generation-)
|
||||||
let deviceTypeIdentifier: String?
|
public let deviceTypeIdentifier: String?
|
||||||
|
|
||||||
/// Device state (e.g. Shutdown)
|
/// Device state (e.g. Shutdown)
|
||||||
let state: String
|
public let state: String
|
||||||
|
|
||||||
/// Returns true if the device is shutdown.
|
/// Returns true if the device is shutdown.
|
||||||
var isShutdown: Bool {
|
public var isShutdown: Bool {
|
||||||
state == "Shutdown"
|
state == "Shutdown"
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Device name (e.g. iPad Air (3rd generation))
|
/// Device name (e.g. iPad Air (3rd generation))
|
||||||
let name: String
|
public let name: String
|
||||||
|
|
||||||
/// If the device is not available, this provides a description of the error.
|
/// If the device is not available, this provides a description of the error.
|
||||||
let availabilityError: String?
|
public let availabilityError: String?
|
||||||
|
|
||||||
/// Device runtime identifier (e.g. com.apple.CoreSimulator.SimRuntime.iOS-13-5)
|
/// Device runtime identifier (e.g. com.apple.CoreSimulator.SimRuntime.iOS-13-5)
|
||||||
let runtimeIdentifier: String
|
public let runtimeIdentifier: String
|
||||||
|
|
||||||
var description: String {
|
public var description: String {
|
||||||
name
|
name
|
||||||
}
|
}
|
||||||
|
|
||||||
public init(dataPath: AbsolutePath,
|
public init(
|
||||||
|
dataPath: AbsolutePath,
|
||||||
logPath: AbsolutePath,
|
logPath: AbsolutePath,
|
||||||
udid: String,
|
udid: String,
|
||||||
isAvailable: Bool,
|
isAvailable: Bool,
|
||||||
|
@ -47,8 +48,8 @@ struct SimulatorDevice: Decodable, Hashable, CustomStringConvertible {
|
||||||
state: String,
|
state: String,
|
||||||
name: String,
|
name: String,
|
||||||
availabilityError: String?,
|
availabilityError: String?,
|
||||||
runtimeIdentifier: String)
|
runtimeIdentifier: String
|
||||||
{
|
) {
|
||||||
self.dataPath = dataPath
|
self.dataPath = dataPath
|
||||||
self.logPath = logPath
|
self.logPath = logPath
|
||||||
self.udid = udid
|
self.udid = udid
|
|
@ -0,0 +1,9 @@
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
public struct SimulatorDeviceAndRuntime: Hashable {
|
||||||
|
/// Device
|
||||||
|
public let device: SimulatorDevice
|
||||||
|
|
||||||
|
/// Device's runtime.
|
||||||
|
public let runtime: SimulatorRuntime
|
||||||
|
}
|
|
@ -3,36 +3,37 @@ import TSCBasic
|
||||||
|
|
||||||
/// It represents a runtime that is available in the system. The list of available runtimes is obtained
|
/// It represents a runtime that is available in the system. The list of available runtimes is obtained
|
||||||
/// using Xcode's simctl cli tool.
|
/// using Xcode's simctl cli tool.
|
||||||
struct SimulatorRuntime: Decodable, Hashable, CustomStringConvertible {
|
public struct SimulatorRuntime: Decodable, Hashable, CustomStringConvertible {
|
||||||
/// Runtime bundle path (e.g. /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Library/Developer/CoreSimulator/Profiles/Runtimes/iOS.simruntime)
|
/// Runtime bundle path (e.g. /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Library/Developer/CoreSimulator/Profiles/Runtimes/iOS.simruntime)
|
||||||
let bundlePath: AbsolutePath
|
public let bundlePath: AbsolutePath
|
||||||
|
|
||||||
/// Runtime build version (e.g. 17F61)
|
/// Runtime build version (e.g. 17F61)
|
||||||
let buildVersion: String
|
public let buildVersion: String
|
||||||
|
|
||||||
/// Runtime root (e.g. /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Library/Developer/CoreSimulator/Profiles/Runtimes\/iOS.simruntime\Contents/Resources/RuntimeRoot)
|
/// Runtime root (e.g. /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Library/Developer/CoreSimulator/Profiles/Runtimes\/iOS.simruntime\Contents/Resources/RuntimeRoot)
|
||||||
let runtimeRoot: AbsolutePath
|
public let runtimeRoot: AbsolutePath
|
||||||
|
|
||||||
/// Runtime identifier (e.g. com.apple.CoreSimulator.SimRuntime.iOS-13-5)
|
/// Runtime identifier (e.g. com.apple.CoreSimulator.SimRuntime.iOS-13-5)
|
||||||
let identifier: String
|
public let identifier: String
|
||||||
|
|
||||||
/// Runtime version (e.g. 13.5)
|
/// Runtime version (e.g. 13.5)
|
||||||
let version: SimulatorRuntimeVersion
|
public let version: SimulatorRuntimeVersion
|
||||||
|
|
||||||
// True if the runtime is available.
|
// True if the runtime is available.
|
||||||
let isAvailable: Bool
|
public let isAvailable: Bool
|
||||||
|
|
||||||
// Name of the runtime (e.g. iOS 13.5)
|
// Name of the runtime (e.g. iOS 13.5)
|
||||||
let name: String
|
public let name: String
|
||||||
|
|
||||||
init(bundlePath: AbsolutePath,
|
public init(
|
||||||
|
bundlePath: AbsolutePath,
|
||||||
buildVersion: String,
|
buildVersion: String,
|
||||||
runtimeRoot: AbsolutePath,
|
runtimeRoot: AbsolutePath,
|
||||||
identifier: String,
|
identifier: String,
|
||||||
version: SimulatorRuntimeVersion,
|
version: SimulatorRuntimeVersion,
|
||||||
isAvailable: Bool,
|
isAvailable: Bool,
|
||||||
name: String)
|
name: String
|
||||||
{
|
) {
|
||||||
self.bundlePath = bundlePath
|
self.bundlePath = bundlePath
|
||||||
self.buildVersion = buildVersion
|
self.buildVersion = buildVersion
|
||||||
self.runtimeRoot = runtimeRoot
|
self.runtimeRoot = runtimeRoot
|
||||||
|
@ -52,7 +53,7 @@ struct SimulatorRuntime: Decodable, Hashable, CustomStringConvertible {
|
||||||
case name
|
case name
|
||||||
}
|
}
|
||||||
|
|
||||||
var description: String {
|
public var description: String {
|
||||||
name
|
name
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,11 +1,11 @@
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
struct SimulatorRuntimeVersion: CustomStringConvertible, Hashable, ExpressibleByStringLiteral, Comparable, Decodable {
|
public struct SimulatorRuntimeVersion: CustomStringConvertible, Hashable, ExpressibleByStringLiteral, Comparable, Decodable {
|
||||||
// MARK: - Attributes
|
// MARK: - Attributes
|
||||||
|
|
||||||
let major: Int
|
public let major: Int
|
||||||
let minor: Int?
|
public let minor: Int?
|
||||||
let patch: Int?
|
public let patch: Int?
|
||||||
|
|
||||||
// MARK: - Constructors
|
// MARK: - Constructors
|
||||||
|
|
||||||
|
@ -15,7 +15,7 @@ struct SimulatorRuntimeVersion: CustomStringConvertible, Hashable, ExpressibleBy
|
||||||
self.patch = patch
|
self.patch = patch
|
||||||
}
|
}
|
||||||
|
|
||||||
init(from decoder: Decoder) throws {
|
public init(from decoder: Decoder) throws {
|
||||||
let container = try decoder.singleValueContainer()
|
let container = try decoder.singleValueContainer()
|
||||||
self.init(stringLiteral: try container.decode(String.self))
|
self.init(stringLiteral: try container.decode(String.self))
|
||||||
}
|
}
|
||||||
|
@ -30,7 +30,7 @@ struct SimulatorRuntimeVersion: CustomStringConvertible, Hashable, ExpressibleBy
|
||||||
|
|
||||||
// MARK: - CustomStringConvertible
|
// MARK: - CustomStringConvertible
|
||||||
|
|
||||||
var description: String {
|
public var description: String {
|
||||||
var version = "\(major)"
|
var version = "\(major)"
|
||||||
if let minor = minor {
|
if let minor = minor {
|
||||||
version.append(".\(minor)")
|
version.append(".\(minor)")
|
||||||
|
@ -47,7 +47,7 @@ struct SimulatorRuntimeVersion: CustomStringConvertible, Hashable, ExpressibleBy
|
||||||
|
|
||||||
// MARK: - Equatable
|
// MARK: - Equatable
|
||||||
|
|
||||||
static func == (lhs: SimulatorRuntimeVersion, rhs: SimulatorRuntimeVersion) -> Bool {
|
public static func == (lhs: SimulatorRuntimeVersion, rhs: SimulatorRuntimeVersion) -> Bool {
|
||||||
lhs.major == rhs.major &&
|
lhs.major == rhs.major &&
|
||||||
lhs.minor == rhs.minor &&
|
lhs.minor == rhs.minor &&
|
||||||
lhs.patch == rhs.patch
|
lhs.patch == rhs.patch
|
||||||
|
@ -55,7 +55,7 @@ struct SimulatorRuntimeVersion: CustomStringConvertible, Hashable, ExpressibleBy
|
||||||
|
|
||||||
// MARK: - Comparable
|
// MARK: - Comparable
|
||||||
|
|
||||||
static func < (lhs: SimulatorRuntimeVersion, rhs: SimulatorRuntimeVersion) -> Bool {
|
public static func < (lhs: SimulatorRuntimeVersion, rhs: SimulatorRuntimeVersion) -> Bool {
|
||||||
let lhs = lhs.flattened()
|
let lhs = lhs.flattened()
|
||||||
let rhs = rhs.flattened()
|
let rhs = rhs.flattened()
|
||||||
|
|
||||||
|
@ -76,7 +76,7 @@ struct SimulatorRuntimeVersion: CustomStringConvertible, Hashable, ExpressibleBy
|
||||||
|
|
||||||
// MARK: - ExpressibleByStringLiteral
|
// MARK: - ExpressibleByStringLiteral
|
||||||
|
|
||||||
init(stringLiteral value: String) {
|
public init(stringLiteral value: String) {
|
||||||
let components = value.split(separator: ".")
|
let components = value.split(separator: ".")
|
||||||
|
|
||||||
// Major
|
// Major
|
|
@ -72,7 +72,7 @@ public struct ValueGraph: Equatable {
|
||||||
if let targetNode = node as? TargetNode {
|
if let targetNode = node as? TargetNode {
|
||||||
targetNode.dependencies.forEach { nodeDependencies.formUnion([self.dependency(from: $0)]) }
|
targetNode.dependencies.forEach { nodeDependencies.formUnion([self.dependency(from: $0)]) }
|
||||||
} else if let frameworkNode = node as? FrameworkNode {
|
} else if let frameworkNode = node as? FrameworkNode {
|
||||||
frameworkNode.dependencies.forEach { nodeDependencies.formUnion([self.dependency(from: $0)]) }
|
frameworkNode.dependencies.forEach { nodeDependencies.formUnion([self.dependency(from: $0.node)]) }
|
||||||
} else if let xcframeworkNode = node as? XCFrameworkNode {
|
} else if let xcframeworkNode = node as? XCFrameworkNode {
|
||||||
xcframeworkNode.dependencies.forEach { nodeDependencies.formUnion([self.dependency(from: $0.node)]) }
|
xcframeworkNode.dependencies.forEach { nodeDependencies.formUnion([self.dependency(from: $0.node)]) }
|
||||||
}
|
}
|
||||||
|
|
|
@ -71,6 +71,11 @@ public class ValueGraphTraverser: GraphTraversing {
|
||||||
.sorted()
|
.sorted()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public func appClipsDependency(path: AbsolutePath, name: String) -> Target? {
|
||||||
|
directTargetDependencies(path: path, name: name)
|
||||||
|
.first { $0.product == .appClip }
|
||||||
|
}
|
||||||
|
|
||||||
public func directStaticDependencies(path: AbsolutePath, name: String) -> [GraphDependencyReference] {
|
public func directStaticDependencies(path: AbsolutePath, name: String) -> [GraphDependencyReference] {
|
||||||
graph.dependencies[.target(name: name, path: path)]?
|
graph.dependencies[.target(name: name, path: path)]?
|
||||||
.compactMap { (dependency: ValueGraphDependency) -> (path: AbsolutePath, name: String)? in
|
.compactMap { (dependency: ValueGraphDependency) -> (path: AbsolutePath, name: String)? in
|
||||||
|
|
|
@ -19,6 +19,21 @@ final class MockXcodeBuildController: XcodeBuildControlling {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var testStub: ((XcodeBuildTarget, String, Bool, XcodeBuildDestination, [XcodeBuildArgument]) -> Observable<SystemEvent<XcodeBuildOutput>>)?
|
||||||
|
func test(
|
||||||
|
_ target: XcodeBuildTarget,
|
||||||
|
scheme: String,
|
||||||
|
clean: Bool,
|
||||||
|
destination: XcodeBuildDestination,
|
||||||
|
arguments: [XcodeBuildArgument]
|
||||||
|
) -> Observable<SystemEvent<XcodeBuildOutput>> {
|
||||||
|
if let testStub = testStub {
|
||||||
|
return testStub(target, scheme, clean, destination, arguments)
|
||||||
|
} else {
|
||||||
|
return Observable.error(TestError("\(String(describing: MockXcodeBuildController.self)) received an unexpected call to test"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var archiveStub: ((XcodeBuildTarget, String, Bool, AbsolutePath, [XcodeBuildArgument]) -> Observable<SystemEvent<XcodeBuildOutput>>)?
|
var archiveStub: ((XcodeBuildTarget, String, Bool, AbsolutePath, [XcodeBuildArgument]) -> Observable<SystemEvent<XcodeBuildOutput>>)?
|
||||||
func archive(_ target: XcodeBuildTarget,
|
func archive(_ target: XcodeBuildTarget,
|
||||||
scheme: String,
|
scheme: String,
|
||||||
|
|
|
@ -9,7 +9,7 @@ public extension FrameworkNode {
|
||||||
bcsymbolmapPaths: [AbsolutePath] = [],
|
bcsymbolmapPaths: [AbsolutePath] = [],
|
||||||
linking: BinaryLinking = .dynamic,
|
linking: BinaryLinking = .dynamic,
|
||||||
architectures: [BinaryArchitecture] = [],
|
architectures: [BinaryArchitecture] = [],
|
||||||
dependencies: [FrameworkNode] = []) -> FrameworkNode
|
dependencies: [PrecompiledNode.Dependency] = []) -> FrameworkNode
|
||||||
{
|
{
|
||||||
FrameworkNode(path: path,
|
FrameworkNode(path: path,
|
||||||
dsymPath: dsymPath,
|
dsymPath: dsymPath,
|
||||||
|
|
|
@ -3,23 +3,28 @@ import TSCBasic
|
||||||
@testable import TuistCore
|
@testable import TuistCore
|
||||||
|
|
||||||
public extension Graph {
|
public extension Graph {
|
||||||
static func test(name: String = "test",
|
static func test(
|
||||||
|
name: String = "test",
|
||||||
entryPath: AbsolutePath = AbsolutePath("/test/graph"),
|
entryPath: AbsolutePath = AbsolutePath("/test/graph"),
|
||||||
entryNodes: [GraphNode] = [],
|
entryNodes: [GraphNode] = [],
|
||||||
|
workspace: Workspace = Workspace.test(path: "/test/graph"),
|
||||||
projects: [Project] = [],
|
projects: [Project] = [],
|
||||||
cocoapods: [CocoaPodsNode] = [],
|
cocoapods: [CocoaPodsNode] = [],
|
||||||
packages: [PackageNode] = [],
|
packages: [PackageNode] = [],
|
||||||
precompiled: [PrecompiledNode] = [],
|
precompiled: [PrecompiledNode] = [],
|
||||||
targets: [AbsolutePath: [TargetNode]] = [:]) -> Graph
|
targets: [AbsolutePath: [TargetNode]] = [:]
|
||||||
{
|
) -> Graph {
|
||||||
Graph(name: name,
|
Graph(
|
||||||
|
name: name,
|
||||||
entryPath: entryPath,
|
entryPath: entryPath,
|
||||||
entryNodes: entryNodes,
|
entryNodes: entryNodes,
|
||||||
|
workspace: workspace,
|
||||||
projects: projects,
|
projects: projects,
|
||||||
cocoapods: cocoapods,
|
cocoapods: cocoapods,
|
||||||
packages: packages,
|
packages: packages,
|
||||||
precompiled: precompiled,
|
precompiled: precompiled,
|
||||||
targets: targets)
|
targets: targets
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates a test dependency graph for targets within a single project
|
/// Creates a test dependency graph for targets within a single project
|
||||||
|
|
|
@ -10,9 +10,9 @@ public final class MockGraphLoader: GraphLoading {
|
||||||
return try loadProjectStub?(path) ?? (Graph.test(), Project.test())
|
return try loadProjectStub?(path) ?? (Graph.test(), Project.test())
|
||||||
}
|
}
|
||||||
|
|
||||||
public var loadWorkspaceStub: ((AbsolutePath) throws -> (Graph, Workspace))?
|
public var loadWorkspaceStub: ((AbsolutePath) throws -> (Graph))?
|
||||||
public func loadWorkspace(path: AbsolutePath) throws -> (Graph, Workspace) {
|
public func loadWorkspace(path: AbsolutePath) throws -> (Graph) {
|
||||||
return try loadWorkspaceStub?(path) ?? (Graph.test(), Workspace.test())
|
return try loadWorkspaceStub?(path) ?? Graph.test()
|
||||||
}
|
}
|
||||||
|
|
||||||
public var loadConfigStub: ((AbsolutePath) throws -> (Config))?
|
public var loadConfigStub: ((AbsolutePath) throws -> (Config))?
|
||||||
|
|
|
@ -6,10 +6,12 @@ import TSCBasic
|
||||||
public extension TargetNode {
|
public extension TargetNode {
|
||||||
static func test(project: Project = .test(),
|
static func test(project: Project = .test(),
|
||||||
target: Target = .test(),
|
target: Target = .test(),
|
||||||
dependencies: [GraphNode] = []) -> TargetNode
|
dependencies: [GraphNode] = [],
|
||||||
|
prune: Bool = false) -> TargetNode
|
||||||
{
|
{
|
||||||
TargetNode(project: project,
|
TargetNode(project: project,
|
||||||
target: target,
|
target: target,
|
||||||
dependencies: dependencies)
|
dependencies: dependencies,
|
||||||
|
prune: prune)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,7 @@ public extension XCFrameworkNode {
|
||||||
infoPlist: XCFrameworkInfoPlist = .test(),
|
infoPlist: XCFrameworkInfoPlist = .test(),
|
||||||
primaryBinaryPath: AbsolutePath = "/MyFramework/MyFramework.xcframework/binary",
|
primaryBinaryPath: AbsolutePath = "/MyFramework/MyFramework.xcframework/binary",
|
||||||
linking: BinaryLinking = .dynamic,
|
linking: BinaryLinking = .dynamic,
|
||||||
dependencies: [XCFrameworkNode.Dependency] = []) -> XCFrameworkNode
|
dependencies: [PrecompiledNode.Dependency] = []) -> XCFrameworkNode
|
||||||
{
|
{
|
||||||
XCFrameworkNode(path: path,
|
XCFrameworkNode(path: path,
|
||||||
infoPlist: infoPlist,
|
infoPlist: infoPlist,
|
||||||
|
|
|
@ -100,4 +100,8 @@ final class MockGraphTraverser: GraphTraversing {
|
||||||
invokedDirectStaticDependenciesParametersList.append((path, name))
|
invokedDirectStaticDependenciesParametersList.append((path, name))
|
||||||
return stubbedDirectStaticDependenciesResult
|
return stubbedDirectStaticDependenciesResult
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func appClipsDependency(path _: AbsolutePath, name _: String) -> Target? {
|
||||||
|
nil
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,10 +5,12 @@ import TSCBasic
|
||||||
public extension Config {
|
public extension Config {
|
||||||
static func test(compatibleXcodeVersions: CompatibleXcodeVersions = .all,
|
static func test(compatibleXcodeVersions: CompatibleXcodeVersions = .all,
|
||||||
cloud: Cloud? = Cloud.test(),
|
cloud: Cloud? = Cloud.test(),
|
||||||
generationOptions: [GenerationOption] = []) -> Config
|
generationOptions: [GenerationOption] = [],
|
||||||
|
path: AbsolutePath? = nil) -> Config
|
||||||
{
|
{
|
||||||
Config(compatibleXcodeVersions: compatibleXcodeVersions,
|
Config(compatibleXcodeVersions: compatibleXcodeVersions,
|
||||||
cloud: cloud,
|
cloud: cloud,
|
||||||
generationOptions: generationOptions)
|
generationOptions: generationOptions,
|
||||||
|
path: path)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,7 @@ public extension Project {
|
||||||
xcodeProjPath: AbsolutePath = AbsolutePath("/Project/Project.xcodeproj"),
|
xcodeProjPath: AbsolutePath = AbsolutePath("/Project/Project.xcodeproj"),
|
||||||
name: String = "Project",
|
name: String = "Project",
|
||||||
organizationName: String? = nil,
|
organizationName: String? = nil,
|
||||||
|
developmentRegion: String? = nil,
|
||||||
settings: Settings = Settings.test(),
|
settings: Settings = Settings.test(),
|
||||||
filesGroup: ProjectGroup = .group(name: "Project"),
|
filesGroup: ProjectGroup = .group(name: "Project"),
|
||||||
targets: [Target] = [Target.test()],
|
targets: [Target] = [Target.test()],
|
||||||
|
@ -20,6 +21,7 @@ public extension Project {
|
||||||
xcodeProjPath: xcodeProjPath,
|
xcodeProjPath: xcodeProjPath,
|
||||||
name: name,
|
name: name,
|
||||||
organizationName: organizationName,
|
organizationName: organizationName,
|
||||||
|
developmentRegion: developmentRegion,
|
||||||
settings: settings,
|
settings: settings,
|
||||||
filesGroup: filesGroup,
|
filesGroup: filesGroup,
|
||||||
targets: targets,
|
targets: targets,
|
||||||
|
@ -33,6 +35,7 @@ public extension Project {
|
||||||
xcodeProjPath: AbsolutePath = AbsolutePath("/test/text.xcodeproj"),
|
xcodeProjPath: AbsolutePath = AbsolutePath("/test/text.xcodeproj"),
|
||||||
name: String = "Project",
|
name: String = "Project",
|
||||||
organizationName: String? = nil,
|
organizationName: String? = nil,
|
||||||
|
developmentRegion: String? = nil,
|
||||||
settings: Settings = .default,
|
settings: Settings = .default,
|
||||||
filesGroup: ProjectGroup = .group(name: "Project"),
|
filesGroup: ProjectGroup = .group(name: "Project"),
|
||||||
targets: [Target] = [],
|
targets: [Target] = [],
|
||||||
|
@ -45,6 +48,7 @@ public extension Project {
|
||||||
xcodeProjPath: xcodeProjPath,
|
xcodeProjPath: xcodeProjPath,
|
||||||
name: name,
|
name: name,
|
||||||
organizationName: organizationName,
|
organizationName: organizationName,
|
||||||
|
developmentRegion: developmentRegion,
|
||||||
settings: settings,
|
settings: settings,
|
||||||
filesGroup: filesGroup,
|
filesGroup: filesGroup,
|
||||||
targets: targets,
|
targets: targets,
|
||||||
|
|
|
@ -21,7 +21,8 @@ public extension Target {
|
||||||
actions: [TargetAction] = [],
|
actions: [TargetAction] = [],
|
||||||
environment: [String: String] = [:],
|
environment: [String: String] = [:],
|
||||||
filesGroup: ProjectGroup = .group(name: "Project"),
|
filesGroup: ProjectGroup = .group(name: "Project"),
|
||||||
dependencies: [Dependency] = []) -> Target
|
dependencies: [Dependency] = [],
|
||||||
|
scripts: [TargetScript] = []) -> Target
|
||||||
{
|
{
|
||||||
Target(name: name,
|
Target(name: name,
|
||||||
platform: platform,
|
platform: platform,
|
||||||
|
@ -39,7 +40,8 @@ public extension Target {
|
||||||
actions: actions,
|
actions: actions,
|
||||||
environment: environment,
|
environment: environment,
|
||||||
filesGroup: filesGroup,
|
filesGroup: filesGroup,
|
||||||
dependencies: dependencies)
|
dependencies: dependencies,
|
||||||
|
scripts: scripts)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates a bare bones Target with as little data as possible
|
/// Creates a bare bones Target with as little data as possible
|
||||||
|
@ -59,7 +61,8 @@ public extension Target {
|
||||||
actions: [TargetAction] = [],
|
actions: [TargetAction] = [],
|
||||||
environment: [String: String] = [:],
|
environment: [String: String] = [:],
|
||||||
filesGroup: ProjectGroup = .group(name: "Project"),
|
filesGroup: ProjectGroup = .group(name: "Project"),
|
||||||
dependencies: [Dependency] = []) -> Target
|
dependencies: [Dependency] = [],
|
||||||
|
scripts: [TargetScript] = []) -> Target
|
||||||
{
|
{
|
||||||
Target(name: name,
|
Target(name: name,
|
||||||
platform: platform,
|
platform: platform,
|
||||||
|
@ -77,6 +80,7 @@ public extension Target {
|
||||||
actions: actions,
|
actions: actions,
|
||||||
environment: environment,
|
environment: environment,
|
||||||
filesGroup: filesGroup,
|
filesGroup: filesGroup,
|
||||||
dependencies: dependencies)
|
dependencies: dependencies,
|
||||||
|
scripts: scripts)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,13 @@
|
||||||
|
import Foundation
|
||||||
|
import TSCBasic
|
||||||
|
@testable import TuistCore
|
||||||
|
|
||||||
|
public extension TargetScript {
|
||||||
|
static func test(name: String = "Test",
|
||||||
|
script: String = "",
|
||||||
|
showEnvVarsInLog: Bool = false,
|
||||||
|
hashable: Bool = false) -> TargetScript
|
||||||
|
{
|
||||||
|
TargetScript(name: name, script: script, showEnvVarsInLog: showEnvVarsInLog, hashable: hashable)
|
||||||
|
}
|
||||||
|
}
|
|
@ -3,14 +3,19 @@ import TSCBasic
|
||||||
@testable import TuistCore
|
@testable import TuistCore
|
||||||
|
|
||||||
public extension Workspace {
|
public extension Workspace {
|
||||||
static func test(path: AbsolutePath = AbsolutePath("/"),
|
static func test(
|
||||||
|
path: AbsolutePath = AbsolutePath("/"),
|
||||||
name: String = "test",
|
name: String = "test",
|
||||||
projects: [AbsolutePath] = [],
|
projects: [AbsolutePath] = [],
|
||||||
additionalFiles: [FileElement] = []) -> Workspace
|
schemes: [Scheme] = [],
|
||||||
{
|
additionalFiles: [FileElement] = []
|
||||||
Workspace(path: path,
|
) -> Workspace {
|
||||||
|
Workspace(
|
||||||
|
path: path,
|
||||||
name: name,
|
name: name,
|
||||||
projects: projects,
|
projects: projects,
|
||||||
additionalFiles: additionalFiles)
|
schemes: schemes,
|
||||||
|
additionalFiles: additionalFiles
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,10 @@
|
||||||
import Foundation
|
import Foundation
|
||||||
import RxSwift
|
import RxSwift
|
||||||
|
import struct TSCUtility.Version
|
||||||
|
import TuistCore
|
||||||
import TuistSupport
|
import TuistSupport
|
||||||
|
|
||||||
@testable import TuistAutomation
|
@testable import TuistCore
|
||||||
@testable import TuistSupportTesting
|
@testable import TuistSupportTesting
|
||||||
|
|
||||||
public final class MockSimulatorController: SimulatorControlling {
|
public final class MockSimulatorController: SimulatorControlling {
|
||||||
|
@ -43,4 +45,9 @@ public final class MockSimulatorController: SimulatorControlling {
|
||||||
return .error(TestError("call to non-stubbed method runtimesAndDevices"))
|
return .error(TestError("call to non-stubbed method runtimesAndDevices"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public var findAvailableDeviceStub: ((Platform, Version?, Version?, String?) -> Single<SimulatorDevice>)?
|
||||||
|
public func findAvailableDevice(platform: Platform, version: Version?, minVersion: Version?, deviceName: String?) -> Single<SimulatorDevice> {
|
||||||
|
findAvailableDeviceStub?(platform, version, minVersion, deviceName) ?? .just(SimulatorDevice.test())
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -1,6 +1,6 @@
|
||||||
import Foundation
|
import Foundation
|
||||||
import TSCBasic
|
import TSCBasic
|
||||||
@testable import TuistAutomation
|
@testable import TuistCore
|
||||||
|
|
||||||
extension SimulatorDevice {
|
extension SimulatorDevice {
|
||||||
static func test(dataPath: AbsolutePath = "/Library/Developer/CoreSimulator/Devices/3A8C9673-C1FD-4E33-8EFA-AEEBF43161CC/data",
|
static func test(dataPath: AbsolutePath = "/Library/Developer/CoreSimulator/Devices/3A8C9673-C1FD-4E33-8EFA-AEEBF43161CC/data",
|
|
@ -1,6 +1,6 @@
|
||||||
import Foundation
|
import Foundation
|
||||||
import TSCBasic
|
import TSCBasic
|
||||||
@testable import TuistAutomation
|
@testable import TuistCore
|
||||||
|
|
||||||
extension SimulatorDeviceAndRuntime {
|
extension SimulatorDeviceAndRuntime {
|
||||||
static func test(device: SimulatorDevice = .test(),
|
static func test(device: SimulatorDevice = .test(),
|
|
@ -1,6 +1,6 @@
|
||||||
import Foundation
|
import Foundation
|
||||||
import TSCBasic
|
import TSCBasic
|
||||||
@testable import TuistAutomation
|
@testable import TuistCore
|
||||||
|
|
||||||
extension SimulatorRuntime {
|
extension SimulatorRuntime {
|
||||||
// swiftlint:disable:next line_length
|
// swiftlint:disable:next line_length
|
|
@ -3,8 +3,6 @@ import TSCBasic
|
||||||
@testable import TuistCore
|
@testable import TuistCore
|
||||||
|
|
||||||
public final class MockRootDirectoryLocator: RootDirectoryLocating {
|
public final class MockRootDirectoryLocator: RootDirectoryLocating {
|
||||||
public init() {}
|
|
||||||
|
|
||||||
public var locateArgs: [AbsolutePath] = []
|
public var locateArgs: [AbsolutePath] = []
|
||||||
public var locateStub: AbsolutePath?
|
public var locateStub: AbsolutePath?
|
||||||
|
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue