How to optionally link swift framework into another framework


(Jakub Bednář) #1

Hello everyone,

I am trying to add optional logging into my framework, but for sake of this question, lets assume I want to add optional logging into an app. I have created an example with following setup:

1. Logging.framework declares

  public protocol Logging {
    func debug(message: String)
  }

  and I have build the framework for the app to see it.

2. Application has

  import Logging
  
  public class Engine {

    let logger: Logging?

    public init(withLogger logger: Logging? = nil) {
      self.logger = logger
    }

    public work() {
      self.logger?.debug(“Working”)
    }
  }

Now I don’t have the Logging.framework in Embed Binaries nor Link Frameworks lists. My app builds ok, but then fails to start telling me that Logging.framework was not loaded. I checked the binary using otool -L and Logging.framework is still referenced by the binary. Is there any way how to achieve my goal? This would be trivial with Objective-C and I still can’t figure it out in Swift.

Thanks a lot,

J.


(Rien) #2

You need conditional compilation.

Called “Active Compilation Conditions” in the build settings.

For example define a ACC of “USE_LOGGING”

Then in your code:

#if USE_LOGGING

import Logging

#else

struct Logging {
  func debug(message: string) {}
}

#endif

Regards,
Rien

Site: http://balancingrock.nl
Blog: http://swiftrien.blogspot.com
Github: http://github.com/Balancingrock
Project: http://swiftfire.nl

···

On 06 Mar 2017, at 17:26, Jakub Bednář via swift-users <swift-users@swift.org> wrote:

Hello everyone,

I am trying to add optional logging into my framework, but for sake of this question, lets assume I want to add optional logging into an app. I have created an example with following setup:

1. Logging.framework declares

  public protocol Logging {
    func debug(message: String)
  }

  and I have build the framework for the app to see it.

2. Application has

  import Logging
  
  public class Engine {

    let logger: Logging?

    public init(withLogger logger: Logging? = nil) {
      self.logger = logger
    }

    public work() {
      self.logger?.debug(“Working”)
    }
  }

Now I don’t have the Logging.framework in Embed Binaries nor Link Frameworks lists. My app builds ok, but then fails to start telling me that Logging.framework was not loaded. I checked the binary using otool -L and Logging.framework is still referenced by the binary. Is there any way how to achieve my goal? This would be trivial with Objective-C and I still can’t figure it out in Swift.

Thanks a lot,

J.

_______________________________________________
swift-users mailing list
swift-users@swift.org
https://lists.swift.org/mailman/listinfo/swift-users


(Rien) #3

Well, that was a bit short…

When you want to use logging, define the ACC “USE_LOGGING” in the build settings.
When you don’t want to use logging, don’t define the ACC.

PS: You can get my logging framework from github: https://github.com/Balancingrock/SwifterLog

Regards,
Rien

Site: http://balancingrock.nl
Blog: http://swiftrien.blogspot.com
Github: http://github.com/Balancingrock
Project: http://swiftfire.nl

···

On 06 Mar 2017, at 17:58, Rien <Rien@Balancingrock.nl> wrote:

You need conditional compilation.

Called “Active Compilation Conditions” in the build settings.

For example define a ACC of “USE_LOGGING”

Then in your code:

#if USE_LOGGING

import Logging

#else

struct Logging {
  func debug(message: string) {}
}

#endif

Regards,
Rien

Site: http://balancingrock.nl
Blog: http://swiftrien.blogspot.com
Github: http://github.com/Balancingrock
Project: http://swiftfire.nl

On 06 Mar 2017, at 17:26, Jakub Bednář via swift-users <swift-users@swift.org> wrote:

Hello everyone,

I am trying to add optional logging into my framework, but for sake of this question, lets assume I want to add optional logging into an app. I have created an example with following setup:

1. Logging.framework declares

  public protocol Logging {
    func debug(message: String)
  }

  and I have build the framework for the app to see it.

2. Application has

  import Logging
  
  public class Engine {

    let logger: Logging?

    public init(withLogger logger: Logging? = nil) {
      self.logger = logger
    }

    public work() {
      self.logger?.debug(“Working”)
    }
  }

Now I don’t have the Logging.framework in Embed Binaries nor Link Frameworks lists. My app builds ok, but then fails to start telling me that Logging.framework was not loaded. I checked the binary using otool -L and Logging.framework is still referenced by the binary. Is there any way how to achieve my goal? This would be trivial with Objective-C and I still can’t figure it out in Swift.

Thanks a lot,

J.

_______________________________________________
swift-users mailing list
swift-users@swift.org
https://lists.swift.org/mailman/listinfo/swift-users


(Jakub Bednář) #4

Hi Rien,

looks like a good way around my problem.

Still I wonder why this does not work in Swift as it does work in Objective-C or even in strongly typed checked C++.

Maybe it is some kind of Swift’s security measure to avoid hard-to-debug bugs in C++ caused by virtual table inconsistencies.

Anyway, thanks for the help.

J.

···

On Mar 6, 2017, at 6:01 PM, Rien <Rien@Balancingrock.nl> wrote:

Well, that was a bit short…

When you want to use logging, define the ACC “USE_LOGGING” in the build settings.
When you don’t want to use logging, don’t define the ACC.

PS: You can get my logging framework from github: https://github.com/Balancingrock/SwifterLog

Regards,
Rien

Site: http://balancingrock.nl
Blog: http://swiftrien.blogspot.com
Github: http://github.com/Balancingrock
Project: http://swiftfire.nl

On 06 Mar 2017, at 17:58, Rien <Rien@Balancingrock.nl> wrote:

You need conditional compilation.

Called “Active Compilation Conditions” in the build settings.

For example define a ACC of “USE_LOGGING”

Then in your code:

#if USE_LOGGING

import Logging

#else

struct Logging {
  func debug(message: string) {}
}

#endif

Regards,
Rien

Site: http://balancingrock.nl
Blog: http://swiftrien.blogspot.com
Github: http://github.com/Balancingrock
Project: http://swiftfire.nl

On 06 Mar 2017, at 17:26, Jakub Bednář via swift-users <swift-users@swift.org> wrote:

Hello everyone,

I am trying to add optional logging into my framework, but for sake of this question, lets assume I want to add optional logging into an app. I have created an example with following setup:

1. Logging.framework declares

  public protocol Logging {
    func debug(message: String)
  }

  and I have build the framework for the app to see it.

2. Application has

  import Logging
  
  public class Engine {

    let logger: Logging?

    public init(withLogger logger: Logging? = nil) {
      self.logger = logger
    }

    public work() {
      self.logger?.debug(“Working”)
    }
  }

Now I don’t have the Logging.framework in Embed Binaries nor Link Frameworks lists. My app builds ok, but then fails to start telling me that Logging.framework was not loaded. I checked the binary using otool -L and Logging.framework is still referenced by the binary. Is there any way how to achieve my goal? This would be trivial with Objective-C and I still can’t figure it out in Swift.

Thanks a lot,

J.

_______________________________________________
swift-users mailing list
swift-users@swift.org
https://lists.swift.org/mailman/listinfo/swift-users


(Jonathan Prescott) #5

I think the process is the same as C/C++/Obj-C/Obj-C++, except it’s labelled differently for Swift. In Xcode, users set “C Pre-processor Macro Definitions” with things like USE_LOGGING. In Swift, since it doesn’t use the C pre-processor, Xcode has a separate build setting called “Active Compilation Conditions,” as Rien has noted, probably to keeps consistent with the language. Operationally, behavior is the same as C pre-processor defines.

Jonathan

···

On Mar 6, 2017, at 12:09 PM, Jakub Bednář via swift-users <swift-users@swift.org> wrote:

Hi Rien,

looks like a good way around my problem.

Still I wonder why this does not work in Swift as it does work in Objective-C or even in strongly typed checked C++.

Maybe it is some kind of Swift’s security measure to avoid hard-to-debug bugs in C++ caused by virtual table inconsistencies.

Anyway, thanks for the help.

J.

On Mar 6, 2017, at 6:01 PM, Rien <Rien@Balancingrock.nl> wrote:

Well, that was a bit short…

When you want to use logging, define the ACC “USE_LOGGING” in the build settings.
When you don’t want to use logging, don’t define the ACC.

PS: You can get my logging framework from github: https://github.com/Balancingrock/SwifterLog

Regards,
Rien

Site: http://balancingrock.nl
Blog: http://swiftrien.blogspot.com
Github: http://github.com/Balancingrock
Project: http://swiftfire.nl

On 06 Mar 2017, at 17:58, Rien <Rien@Balancingrock.nl> wrote:

You need conditional compilation.

Called “Active Compilation Conditions” in the build settings.

For example define a ACC of “USE_LOGGING”

Then in your code:

#if USE_LOGGING

import Logging

#else

struct Logging {
  func debug(message: string) {}
}

#endif

Regards,
Rien

Site: http://balancingrock.nl
Blog: http://swiftrien.blogspot.com
Github: http://github.com/Balancingrock
Project: http://swiftfire.nl

On 06 Mar 2017, at 17:26, Jakub Bednář via swift-users <swift-users@swift.org> wrote:

Hello everyone,

I am trying to add optional logging into my framework, but for sake of this question, lets assume I want to add optional logging into an app. I have created an example with following setup:

1. Logging.framework declares

  public protocol Logging {
    func debug(message: String)
  }

  and I have build the framework for the app to see it.

2. Application has

  import Logging
  
  public class Engine {

    let logger: Logging?

    public init(withLogger logger: Logging? = nil) {
      self.logger = logger
    }

    public work() {
      self.logger?.debug(“Working”)
    }
  }

Now I don’t have the Logging.framework in Embed Binaries nor Link Frameworks lists. My app builds ok, but then fails to start telling me that Logging.framework was not loaded. I checked the binary using otool -L and Logging.framework is still referenced by the binary. Is there any way how to achieve my goal? This would be trivial with Objective-C and I still can’t figure it out in Swift.

Thanks a lot,

J.

_______________________________________________
swift-users mailing list
swift-users@swift.org
https://lists.swift.org/mailman/listinfo/swift-users

_______________________________________________
swift-users mailing list
swift-users@swift.org
https://lists.swift.org/mailman/listinfo/swift-users


(Rien) #6

The way I look at it is that Swift wants all symbols resolved when linking.
C++ is probably very lenient toward full resolution, and as long as you know that a certain unresolved reference is not used, you can safely ignore the warning.
Or maybe the C++ compiler is just very clever and can figure out that the reference is never used?

Regards,
Rien

Site: http://balancingrock.nl
Blog: http://swiftrien.blogspot.com
Github: http://github.com/Balancingrock
Project: http://swiftfire.nl

···

On 06 Mar 2017, at 18:09, Jakub Bednář <jakub.bednar@avast.com> wrote:

Hi Rien,

looks like a good way around my problem.

Still I wonder why this does not work in Swift as it does work in Objective-C or even in strongly typed checked C++.

Maybe it is some kind of Swift’s security measure to avoid hard-to-debug bugs in C++ caused by virtual table inconsistencies.

Anyway, thanks for the help.

J.

On Mar 6, 2017, at 6:01 PM, Rien <Rien@Balancingrock.nl> wrote:

Well, that was a bit short…

When you want to use logging, define the ACC “USE_LOGGING” in the build settings.
When you don’t want to use logging, don’t define the ACC.

PS: You can get my logging framework from github: https://github.com/Balancingrock/SwifterLog

Regards,
Rien

Site: http://balancingrock.nl
Blog: http://swiftrien.blogspot.com
Github: http://github.com/Balancingrock
Project: http://swiftfire.nl

On 06 Mar 2017, at 17:58, Rien <Rien@Balancingrock.nl> wrote:

You need conditional compilation.

Called “Active Compilation Conditions” in the build settings.

For example define a ACC of “USE_LOGGING”

Then in your code:

#if USE_LOGGING

import Logging

#else

struct Logging {
  func debug(message: string) {}
}

#endif

Regards,
Rien

Site: http://balancingrock.nl
Blog: http://swiftrien.blogspot.com
Github: http://github.com/Balancingrock
Project: http://swiftfire.nl

On 06 Mar 2017, at 17:26, Jakub Bednář via swift-users <swift-users@swift.org> wrote:

Hello everyone,

I am trying to add optional logging into my framework, but for sake of this question, lets assume I want to add optional logging into an app. I have created an example with following setup:

1. Logging.framework declares

  public protocol Logging {
    func debug(message: String)
  }

  and I have build the framework for the app to see it.

2. Application has

  import Logging
  
  public class Engine {

    let logger: Logging?

    public init(withLogger logger: Logging? = nil) {
      self.logger = logger
    }

    public work() {
      self.logger?.debug(“Working”)
    }
  }

Now I don’t have the Logging.framework in Embed Binaries nor Link Frameworks lists. My app builds ok, but then fails to start telling me that Logging.framework was not loaded. I checked the binary using otool -L and Logging.framework is still referenced by the binary. Is there any way how to achieve my goal? This would be trivial with Objective-C and I still can’t figure it out in Swift.

Thanks a lot,

J.

_______________________________________________
swift-users mailing list
swift-users@swift.org
https://lists.swift.org/mailman/listinfo/swift-users


(Jonathan Prescott) #7

In most systems, including macOS, everyone uses the same linker, no matter what language. swiftc, at the object code level, supports the same ABI as clang(C/C++/Obj-C/Objc-C++), and in fact, uses a lot of the compiler infrastructure as clang, including pieces of clang.

Your example is the same as the C pre-processor used in the C-family of languages. The major difference is that the #… processing is performed in the Swift lexical analyzer, and does not support all the myriad functionality of the C pre-processor, whereas conceptually, there is a separate pass of text replacement performed by the pre-processor when compiling C, et al. The conditional compilation is the same between the two, and is affecting the lexical analysis of the program. Has nothing to do with linking, etc..

Jonathan

···

On Mar 6, 2017, at 12:29 PM, Rien via swift-users <swift-users@swift.org> wrote:

The way I look at it is that Swift wants all symbols resolved when linking.
C++ is probably very lenient toward full resolution, and as long as you know that a certain unresolved reference is not used, you can safely ignore the warning.
Or maybe the C++ compiler is just very clever and can figure out that the reference is never used?

Regards,
Rien

Site: http://balancingrock.nl
Blog: http://swiftrien.blogspot.com
Github: http://github.com/Balancingrock
Project: http://swiftfire.nl

On 06 Mar 2017, at 18:09, Jakub Bednář <jakub.bednar@avast.com> wrote:

Hi Rien,

looks like a good way around my problem.

Still I wonder why this does not work in Swift as it does work in Objective-C or even in strongly typed checked C++.

Maybe it is some kind of Swift’s security measure to avoid hard-to-debug bugs in C++ caused by virtual table inconsistencies.

Anyway, thanks for the help.

J.

On Mar 6, 2017, at 6:01 PM, Rien <Rien@Balancingrock.nl> wrote:

Well, that was a bit short…

When you want to use logging, define the ACC “USE_LOGGING” in the build settings.
When you don’t want to use logging, don’t define the ACC.

PS: You can get my logging framework from github: https://github.com/Balancingrock/SwifterLog

Regards,
Rien

Site: http://balancingrock.nl
Blog: http://swiftrien.blogspot.com
Github: http://github.com/Balancingrock
Project: http://swiftfire.nl

On 06 Mar 2017, at 17:58, Rien <Rien@Balancingrock.nl> wrote:

You need conditional compilation.

Called “Active Compilation Conditions” in the build settings.

For example define a ACC of “USE_LOGGING”

Then in your code:

#if USE_LOGGING

import Logging

#else

struct Logging {
  func debug(message: string) {}
}

#endif

Regards,
Rien

Site: http://balancingrock.nl
Blog: http://swiftrien.blogspot.com
Github: http://github.com/Balancingrock
Project: http://swiftfire.nl

On 06 Mar 2017, at 17:26, Jakub Bednář via swift-users <swift-users@swift.org> wrote:

Hello everyone,

I am trying to add optional logging into my framework, but for sake of this question, lets assume I want to add optional logging into an app. I have created an example with following setup:

1. Logging.framework declares

  public protocol Logging {
    func debug(message: String)
  }

  and I have build the framework for the app to see it.

2. Application has

  import Logging
  
  public class Engine {

    let logger: Logging?

    public init(withLogger logger: Logging? = nil) {
      self.logger = logger
    }

    public work() {
      self.logger?.debug(“Working”)
    }
  }

Now I don’t have the Logging.framework in Embed Binaries nor Link Frameworks lists. My app builds ok, but then fails to start telling me that Logging.framework was not loaded. I checked the binary using otool -L and Logging.framework is still referenced by the binary. Is there any way how to achieve my goal? This would be trivial with Objective-C and I still can’t figure it out in Swift.

Thanks a lot,

J.

_______________________________________________
swift-users mailing list
swift-users@swift.org
https://lists.swift.org/mailman/listinfo/swift-users

_______________________________________________
swift-users mailing list
swift-users@swift.org
https://lists.swift.org/mailman/listinfo/swift-users


(Jakub Bednář) #8

C++ puts function pointers (or indexes into virtual function table) into the build binary so it looses track of what exactly it was build against. If in runtime you give it an object that has functions with different signature (types/number of parameters) or different semantics (it does something else), strange things start happening.

Objective-C solved this by remembering selector hashes and looking for the correct method by the selector name rather than using function pointers.

Seems like Swift is doing something different while prohibiting my use case :frowning:

···

On Mar 6, 2017, at 6:29 PM, Rien <Rien@Balancingrock.nl> wrote:

The way I look at it is that Swift wants all symbols resolved when linking.
C++ is probably very lenient toward full resolution, and as long as you know that a certain unresolved reference is not used, you can safely ignore the warning.
Or maybe the C++ compiler is just very clever and can figure out that the reference is never used?

Regards,
Rien

Site: http://balancingrock.nl
Blog: http://swiftrien.blogspot.com
Github: http://github.com/Balancingrock
Project: http://swiftfire.nl

On 06 Mar 2017, at 18:09, Jakub Bednář <jakub.bednar@avast.com> wrote:

Hi Rien,

looks like a good way around my problem.

Still I wonder why this does not work in Swift as it does work in Objective-C or even in strongly typed checked C++.

Maybe it is some kind of Swift’s security measure to avoid hard-to-debug bugs in C++ caused by virtual table inconsistencies.

Anyway, thanks for the help.

J.

On Mar 6, 2017, at 6:01 PM, Rien <Rien@Balancingrock.nl> wrote:

Well, that was a bit short…

When you want to use logging, define the ACC “USE_LOGGING” in the build settings.
When you don’t want to use logging, don’t define the ACC.

PS: You can get my logging framework from github: https://github.com/Balancingrock/SwifterLog

Regards,
Rien

Site: http://balancingrock.nl
Blog: http://swiftrien.blogspot.com
Github: http://github.com/Balancingrock
Project: http://swiftfire.nl

On 06 Mar 2017, at 17:58, Rien <Rien@Balancingrock.nl> wrote:

You need conditional compilation.

Called “Active Compilation Conditions” in the build settings.

For example define a ACC of “USE_LOGGING”

Then in your code:

#if USE_LOGGING

import Logging

#else

struct Logging {
  func debug(message: string) {}
}

#endif

Regards,
Rien

Site: http://balancingrock.nl
Blog: http://swiftrien.blogspot.com
Github: http://github.com/Balancingrock
Project: http://swiftfire.nl

On 06 Mar 2017, at 17:26, Jakub Bednář via swift-users <swift-users@swift.org> wrote:

Hello everyone,

I am trying to add optional logging into my framework, but for sake of this question, lets assume I want to add optional logging into an app. I have created an example with following setup:

1. Logging.framework declares

  public protocol Logging {
    func debug(message: String)
  }

  and I have build the framework for the app to see it.

2. Application has

  import Logging
  
  public class Engine {

    let logger: Logging?

    public init(withLogger logger: Logging? = nil) {
      self.logger = logger
    }

    public work() {
      self.logger?.debug(“Working”)
    }
  }

Now I don’t have the Logging.framework in Embed Binaries nor Link Frameworks lists. My app builds ok, but then fails to start telling me that Logging.framework was not loaded. I checked the binary using otool -L and Logging.framework is still referenced by the binary. Is there any way how to achieve my goal? This would be trivial with Objective-C and I still can’t figure it out in Swift.

Thanks a lot,

J.

_______________________________________________
swift-users mailing list
swift-users@swift.org
https://lists.swift.org/mailman/listinfo/swift-users