Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ venv/
.pydevproject
.settings/*
.ropeproject/*
**/xcuserdata/

.tox
CMakeUserPresets.json
Expand All @@ -69,6 +70,7 @@ tutorial/versioning/revisions/intro/chat/*
tutorial/versioning/revisions/intro/hello/*
examples/libraries/tensorflow-lite/pose-estimation/pose-estimation
examples/libraries/imgui/introduction/bindings
examples/languages/swift/cxx_interop/summer.png

# Bazel files
bazel-*
Expand Down
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ Sources for the [examples section](https://docs.conan.io/2/examples.html) of the

### [Libraries examples](examples/libraries)

### [Language interoperability examples](examples/languages)

### [Graph examples](examples/graph)

### [Security examples](examples/security)
5 changes: 5 additions & 0 deletions examples/languages/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
## Language interoperability examples

### [Swift / C++ interop](swift/cxx_interop)

- Consume plain, Swift-unaware ConanCenter C++ packages directly from a Swift app, using Swift's C++ interoperability mode.
43 changes: 43 additions & 0 deletions examples/languages/swift/cxx_interop/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# Swift / C++ interop with Conan-managed dependencies

A Swift app that consumes a plain, Swift-unaware ConanCenter package --
[lunasvg](https://conan.io/center/recipes/lunasvg) -- directly, via
[Swift's C++ interoperability mode](https://www.swift.org/documentation/cxx-interop/).
Swift builds an SVG scene as a string, hands it to lunasvg's C++ `Document`
and `Bitmap` classes to parse and rasterize, and writes the result to a PNG
file -- no C wrapper library needed.

Since lunasvg ships no Swift module map, `conanfile.py`'s `generate()` writes
a small [Clang module map](https://clang.llvm.org/docs/Modules.html) pointing
at its real installed header (read from `cpp_info`), and sets `OTHER_SWIFT_FLAGS`
through `XcodeToolchain.build_settings` to pass that to `swiftc` via
`-Xcc -fmodule-map-file=...` together with `-cxx-interoperability-mode=default`.
`demo.xcodeproj` is a plain Xcode project whose Release configuration is based
on the `.xcconfig` files that Conan's `XcodeDeps`/`XcodeToolchain` generators
write.

## Requirements

- macOS with Xcode (`swiftc`, `xcodebuild`), Swift 5.9+.
- Conan 2.32 or newer (`XcodeToolchain.build_settings`).

## Build and run

```bash
git clone https://github.com/conan-io/examples2.git
cd examples2/examples/languages/swift/cxx_interop

conan install . -s build_type=Release --build=missing
open demo.xcodeproj
```

From there it is a normal Xcode project: press Run, and Swift calls into
lunasvg. The same build also works from the command line:

```bash
xcodebuild -project demo.xcodeproj -scheme demo -configuration Release \
-derivedDataPath build build
./build/Build/Products/Release/demo
```

The program renders the SVG and writes `summer.png` to the working directory.
17 changes: 17 additions & 0 deletions examples/languages/swift/cxx_interop/ci_test_example.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import platform

from conan.tools.scm import Version

from test.examples_tools import run

print("Swift C++ interop: consuming a plain ConanCenter package from Swift")

conan_version = run("conan --version").split()[-1]

if platform.system() != "Darwin" or Version(conan_version).main < Version("2.32").main:
print("WARNING: Skipping Swift interop example, requires macOS with swiftc and Conan >= 2.32")
else:
run("conan install . -s build_type=Release --build=missing")
run("xcodebuild -project demo.xcodeproj -scheme demo -configuration Release "
"-derivedDataPath build build")
run("./build/Build/Products/Release/demo")
40 changes: 40 additions & 0 deletions examples/languages/swift/cxx_interop/conanfile.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import os
import textwrap
from conan import ConanFile
from conan.tools.apple import XcodeDeps, XcodeToolchain
from conan.tools.build import cppstd_flag
from conan.tools.files import save


class SwiftCppDemo(ConanFile):
settings = "os", "arch", "compiler", "build_type"

def layout(self):
self.folders.generators = "generators"

def requirements(self):
self.requires("lunasvg/3.5.0")

def generate(self):
XcodeDeps(self).generate()

include_dir = self.dependencies["lunasvg"].cpp_info.includedir
header = f"{include_dir}/lunasvg/lunasvg.h"

modulemap_path = os.path.join(self.generators_folder, "lunasvg.modulemap")
modulemap = textwrap.dedent(f'''\
module LunaSVGMod {{
header "{header}"
export *
}}
''')
save(self, modulemap_path, modulemap)

cppstd = cppstd_flag(self)

tc = XcodeToolchain(self)
tc.build_settings["OTHER_SWIFT_FLAGS"] = (
f'$(inherited) -cxx-interoperability-mode=default '
f'-Xcc {cppstd} -Xcc -fmodule-map-file="{modulemap_path}"'
)
tc.generate()
198 changes: 198 additions & 0 deletions examples/languages/swift/cxx_interop/demo.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,198 @@
// !$*UTF8*$!
{
archiveVersion = 1;
classes = {
};
objectVersion = 77;
objects = {

/* Begin PBXBuildFile section */
134B726B92DC37149DD8FB1A /* main.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08895F9843B6FA00C49130BF /* main.swift */; };
/* End PBXBuildFile section */

/* Begin PBXFileReference section */
0507141398EC23BB262BA813 /* demo */ = {isa = PBXFileReference; includeInIndex = 0; path = demo; sourceTree = BUILT_PRODUCTS_DIR; };
08895F9843B6FA00C49130BF /* main.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = main.swift; sourceTree = "<group>"; };
A080ABC67B6FC4B3732B7606 /* conan_config.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = conan_config.xcconfig; sourceTree = "<group>"; };
/* End PBXFileReference section */

/* Begin PBXGroup section */
253DD428CCB2178AB212D9C6 = {
isa = PBXGroup;
children = (
08895F9843B6FA00C49130BF /* main.swift */,
5D676B908F57C1AF87D8AC72 /* generators */,
CBA6EB5BDDD3CD851F85C932 /* Products */,
);
sourceTree = "<group>";
};
5D676B908F57C1AF87D8AC72 /* generators */ = {
isa = PBXGroup;
children = (
A080ABC67B6FC4B3732B7606 /* conan_config.xcconfig */,
);
path = generators;
sourceTree = "<group>";
};
CBA6EB5BDDD3CD851F85C932 /* Products */ = {
isa = PBXGroup;
children = (
0507141398EC23BB262BA813 /* demo */,
);
name = Products;
sourceTree = "<group>";
};
/* End PBXGroup section */

/* Begin PBXNativeTarget section */
25BF8E1F96134B86B979D8A6 /* demo */ = {
isa = PBXNativeTarget;
buildConfigurationList = EED75AB90447BE0300554485 /* Build configuration list for PBXNativeTarget "demo" */;
buildPhases = (
5227B068F7CA093BC3C8FF22 /* Sources */,
);
buildRules = (
);
dependencies = (
);
name = demo;
packageProductDependencies = (
);
productName = demo;
productReference = 0507141398EC23BB262BA813 /* demo */;
productType = "com.apple.product-type.tool";
};
/* End PBXNativeTarget section */

/* Begin PBXProject section */
EE8D0A5E600AE5890BCA0377 /* Project object */ = {
isa = PBXProject;
attributes = {
BuildIndependentTargetsInParallel = YES;
LastUpgradeCheck = 1430;
TargetAttributes = {
};
};
buildConfigurationList = 069786E56D7D9BA850EF6E04 /* Build configuration list for PBXProject "demo" */;
developmentRegion = en;
hasScannedForEncodings = 0;
knownRegions = (
Base,
en,
);
mainGroup = 253DD428CCB2178AB212D9C6;
minimizedProjectReferenceProxies = 1;
preferredProjectObjectVersion = 77;
productRefGroup = CBA6EB5BDDD3CD851F85C932 /* Products */;
projectDirPath = "";
projectRoot = "";
targets = (
25BF8E1F96134B86B979D8A6 /* demo */,
);
};
/* End PBXProject section */

/* Begin PBXSourcesBuildPhase section */
5227B068F7CA093BC3C8FF22 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
134B726B92DC37149DD8FB1A /* main.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */

/* Begin XCBuildConfiguration section */
13750F1439DABFE631C7C9E4 /* Release */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = A080ABC67B6FC4B3732B7606 /* conan_config.xcconfig */;
buildSettings = {
COMBINE_HIDPI_IMAGES = YES;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/../Frameworks",
);
PRODUCT_BUNDLE_IDENTIFIER = io.conan.examples.demo;
SDKROOT = macosx;
};
name = Release;
};
CE5C8F26EC586CE7B2176121 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_ENABLE_OBJC_WEAK = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu11;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
MTL_ENABLE_DEBUG_INFO = NO;
MTL_FAST_MATH = YES;
PRODUCT_NAME = "$(TARGET_NAME)";
SDKROOT = macosx;
SWIFT_COMPILATION_MODE = wholemodule;
SWIFT_OPTIMIZATION_LEVEL = "-O";
SWIFT_VERSION = 5.0;
};
name = Release;
};
/* End XCBuildConfiguration section */

/* Begin XCConfigurationList section */
069786E56D7D9BA850EF6E04 /* Build configuration list for PBXProject "demo" */ = {
isa = XCConfigurationList;
buildConfigurations = (
CE5C8F26EC586CE7B2176121 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
EED75AB90447BE0300554485 /* Build configuration list for PBXNativeTarget "demo" */ = {
isa = XCConfigurationList;
buildConfigurations = (
13750F1439DABFE631C7C9E4 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = EE8D0A5E600AE5890BCA0377 /* Project object */;
}

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

32 changes: 32 additions & 0 deletions examples/languages/swift/cxx_interop/main.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import CxxStdlib
import LunaSVGMod

// lunasvg has no styling of its own; this stylesheet colors the markup below.
let svg = """
<svg xmlns="http://www.w3.org/2000/svg" width="400" height="300">
<rect class="sky" x="0" y="0" width="400" height="230"/>
<polygon class="hills" points="0,230 90,140 170,230 240,160 330,230 400,180 400,230"/>
<rect class="ground" x="0" y="215" width="400" height="85"/>
<g class="cloud" transform="translate(70,55)">
<ellipse cx="0" cy="0" rx="26" ry="14"/>
<ellipse cx="20" cy="-6" rx="20" ry="12"/>
<ellipse cx="-20" cy="4" rx="18" ry="10"/>
</g>
<text class="title" x="200" y="40" text-anchor="middle" font-size="24">Swift + C++ + Conan</text>
<g class="cloud" transform="translate(230,85)">
<ellipse cx="0" cy="0" rx="22" ry="12"/>
<ellipse cx="18" cy="-4" rx="16" ry="10"/>
<ellipse cx="-16" cy="3" rx="15" ry="9"/>
</g>
</svg>
"""

let css = ".sky{fill:#8ECBEB} .hills{fill:#8FA89B} .ground{fill:#8FC77E} .cloud{fill:#FFFFFF} .title{fill:#3B4A40}"

let document = lunasvg.Document.loadFromData(std.string(svg))
document.pointee.applyStyleSheet(std.string(css))

let bitmap = document.pointee.renderToBitmap()
_ = bitmap.writeToPng(std.string("summer.png"))

print("Generated summer.png")
Loading