Some minor points:

1. One major reason for wanting control over which targets are exported are
so packages can control which parts of their API is supported for external
use. This is very important to large projects undergoing active development
while also trying to support a well-defined, semantically versioned API.

2. In keeping with #1, and the design of the Swift language, I think the
right default is for a module to be private/unexported. This is basically
for the same reasons as `internal` is the default access control modifier
in Swift. This also relates to the discussion below.

3. If we are going to add this via a new Target attribute, I suggest we do
so using a `flags` parameter and accompanying enumeration. This will be
equally readable, I think, but more extensible over time.

4. Technically, this proposal is defining two new few features, the
public/private exported/imported part and the ability for packages to
depend on specific targets. They are related problems, but the proposals
(and implementation) are otherwise somewhat orthogonal. I'm fine combining
them into one proposal, but if it appears that one part is going to require
much more design or discussion than the other (or that the combined
discussion is too large) we might consider breaking them up.

There is a larger thing we need to consider with regard to the
public/private part of the proposal, and that is its relation to
hypothetical future support for package-level namespaces, whereby I mean
Swift language support for a new level of namespace (and accompanying
access control support in some form) for an entire Package. There are
several reasons I think that we will ultimately need such a thing, the most
major of which is the module-name collision problem (two packages that want
to use a shared common name for a module) that cannot be resolved by
SwiftPM without language support. Since such a feature is purely
hypothetical at this point, I don't think we need to block forward progress
in SwiftPM on it, but I do think we should discuss the consequences.

Some examples of how these things might be related:

1. The choice of `public/private` is probably wrong when viewed as a
language feature, since they already having existing meanings and
`internal` would be a more accurate name within the context of an
individual Package. I think we should include some discussion of the best
names if we reuse modifier names, or if there is a good argument for
finding alternate names ("exported/unexported"?).

2. Suppose Swift added support for a `package` namespace and access control
modifier. This might make it somewhat awkward if we had tied expectations
of what targets in an external dependency were built to a *manifest*-level
construct.

3. We should call out that public/private in this sense ultimately won't
have any enforcement by the compiler. If a public target exposes a type
declared in a private module implicitly (say via a return value), there
won't be any error, even though it breaks the encapsulation this feature
would partially be intended to provide. Similarly, nothing will prevent a
target from importing a private module from an external dependency if was
already a transitive dependency of some other public target.

It would be good to try and work through some of these issues and lay out
pros & cons as well as other strategies we could use to mitigate them. In
the short term I think we will need to accept and implement some form of
this proposal, so this doesn't mean working out what a full solution would
look like, let's just make sure we aren't (unknowingly) painting ourselves
into a corner.

 - Daniel


On Thu, Jul 7, 2016 at 6:26 AM, Ankit Agarwal via swift-build-dev <
swift-build-...@swift.org> wrote:

> Hi swift packagers,
>
> I am proposing access control to package targets.
>
> Link:
> https://github.com/aciidb0mb3r/swift-evolution/blob/swiftpm-module-access-control/proposals/xxxx-swiftpm-target-access-control.md
>
> Feedback appreciated!
>
> SwiftPM Target Access Control
>
>    - Proposal: SE-XXXX
>    
> <https://github.com/apple/swift-evolution/blob/master/proposals/xxxx-swiftpm-target-access-control.md>
>    - Author: Ankit Aggarwal <https://github.com/aciidb0mb3r>
>    - Status: In Discussion
>    - Review manager: TBD
>
>
> <https://github.com/aciidb0mb3r/swift-evolution/tree/swiftpm-module-access-control#introduction>
> Introduction
>
> This proposal aims to address two issues:
>
>    1.
>
>    Control over the targets exposed (and built) when a SwiftPM package is
>    used as a dependency.
>    2.
>
>    Import (and build) selected targets of a dependency.
>
>
> <https://github.com/aciidb0mb3r/swift-evolution/tree/swiftpm-module-access-control#motivation>
> Motivation
> <https://github.com/aciidb0mb3r/swift-evolution/tree/swiftpm-module-access-control#1-control-over-exposed-targets>1.
> Control over exposed targets:
>
> SwiftPM allows multiple targets (or modules) inside a package. Packages
> usually contain sample usage or example targets which are useful during
> development or testing of the package but are redundant when the package is
> used as a dependency. This increases compile time for the user of the
> package.
>
> As a concrete example: Vapor has a target called Development
> <https://github.com/qutheory/vapor/tree/master/Sources/Development>.
>
> <https://github.com/aciidb0mb3r/swift-evolution/tree/swiftpm-module-access-control#2-import-selected-targets>2.
> Import selected targets:
>
> Sometimes user of a package is only interested in few targets of a
> dependency instead of all the targets. Currently there is no way to state
> this in Package.swift and all the targets are implicitly built and
> exposed to the user package.
>
> For e.g.: I would like to use the targets libc, POSIX, Basic of SwiftPM
> but don't want other targets to be built or exposed in my package.
>
> <https://github.com/aciidb0mb3r/swift-evolution/tree/swiftpm-module-access-control#proposed-solution>Proposed
> Solution
> <https://github.com/aciidb0mb3r/swift-evolution/tree/swiftpm-module-access-control#1-control-over-exposed-targets-1>1.
> Control over exposed targets:
>
> I propose that package authors be able mark the targets they don't want to
> be exposed as private i.e. the privatetargets will be built when that
> package is root package but not when the package is used as a dependency.
>
> To mark a target as private I propose PackageDescription's Target gains a
> isPrivate boolean property which defaults to false.
>
> <https://github.com/aciidb0mb3r/swift-evolution/tree/swiftpm-module-access-control#2-import-selected-targets-1>2.
> Import selected targets:
>
> I propose that package user be able to specify the targets they want to
> import into their package.
>
> To specify the targets to be import I propose to add an optional string
> array property targets in PackageDescription's Package.Dependency which
> defaults to nil i.e. all targets.
>
> Instead of an optional string array property an enum can also be used:
>
> enum ImportedTargets {
>     case allTargets // Import all the targets, default value.
>     case targets([String]) // Import only these targets.
> }
>
>
> <https://github.com/aciidb0mb3r/swift-evolution/tree/swiftpm-module-access-control#detailed-design>Detailed
> Design
> <https://github.com/aciidb0mb3r/swift-evolution/tree/swiftpm-module-access-control#1-control-over-exposed-targets-2>1.
> Control over exposed targets:
>
> Consider a package with following structure:
>
> ├── Package.swift
> └── Sources
>     ├── FooLibrary
>     │   └── Foo.swift
>     └── SampleCLI
>         └── main.swift
>
> The manifest with private target could look like:
>
> import PackageDescription
> let package = Package(
>    name: "FooLibrary",
>    targets: [
>        Target(name: "FooLibrary"),
>        Target(name: "SampleCLI", isPrivate: true),
>    ])
>
> When this package is used as a dependency only FooLibrary is built and is
> importable.
>
> Targets can have other targets as dependency inside a package. A private 
> target
> should only be a dependency to other private targets. For e.g. A manifest
> like this should result in a build failure.
>
> import PackageDescription
> let package = Package(
>    name: "FooLibrary",
>    targets: [
>        Target(name: "FooCore", isPrivate: true),
>        Target(name: "FooLibrary", dependencies: ["FooCore"]), // Error 
> FooCore is private.
>        Target(name: "SampleCLI", dependencies: ["FooCore"], isPrivate: true), 
> // Not an error because SampleCLI is private.
>    ])
>
> Error: FooCore is a private target, it cannot be a dependency to the
> public target FooLibrary.
>
> <https://github.com/aciidb0mb3r/swift-evolution/tree/swiftpm-module-access-control#2-import-selected-targets-2>2.
> Import selected targets:
>
> Consider a dependency with following manifest file:
>
> import PackageDescription
> let package = Package(
>    name: "FooLibrary",
>    targets: [
>        Target(name: "Foo"),
>        Target(name: "Bar", dependencies: ["Foo"]),
>        Target(name: "Baz"),
>    ])
>
> To get only the Bar target from the above package, following manifest
> could be written:
>
> import PackageDescription
> let package = Package(
>    name: "FooUser",
>    dependencies: [
>        .Package(
>            url: "../FooLibrary",
>            majorVersion: 1,
>            targets: ["Bar"])
>    ])
>
> Note: In this case since Bar depends on Foo, Foo will be also be
> implicitly built and be available.
>
> Any target mentioned in targets and not present in the package should
> result in build failure.
>
> <https://github.com/aciidb0mb3r/swift-evolution/tree/swiftpm-module-access-control#impact-on-existing-code>Impact
> on Existing Code
>
> There will be no impact on existing code as these features are additive.
>
> <https://github.com/aciidb0mb3r/swift-evolution/tree/swiftpm-module-access-control#alternatives-considered>Alternatives
> Considered
>
> None at this time.
>
> --
> Ankit
>
>
>
>
> _______________________________________________
> swift-build-dev mailing list
> swift-build-...@swift.org
> https://lists.swift.org/mailman/listinfo/swift-build-dev
>
>
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

Reply via email to