Compiling and interpolating C using the Swift Package Manager


I just finished implementing the C target support in swift package manager which means it is possible to have swift package with C language targets mixed in it (previously only system modules were supported).

To checkout the new functionality lets create two packages:

Factorial

This will be a C only swiftpm package and will act as an external dependency to the second package. Create a directory named Factorial with following structure:

├── Package.swift
└── Sources
    ├── Factorial.c
    └── include
        └── Factorial.h

Factorial.h :

long factorial(int n);

Factorial.c :

#include "include/Factorial.h"

long factorial(int n) {
    if (n == 0 || n == 1) return 1;
    return n * factorial(n-1);
}

We don’t need a modulemap because it will be automatically generated if the include directory structure is supported. These are the supported include structures: (foo is the name of the module)

1. Look for include/foo/foo.h, if so, use umbrella header and error if there are any other dirs or headers outside of there
2. Look for include/foo.h, if so, use umbrella header and error if there are any other dirs outside of there
3. All other cases, umbrella the include dir

If you have a specialized need you can provide a custom modulemap inside include directory.

Finally, place the package under git:

  $ git add . && git commit -m "Initial Commit" && git tag 1.0.0

Thats it, first package is ready.

FactorialRunner

This package will contain 3 targets :

  1. LocalFactorial : This will be a C target which will use the external Factorial package we just created.
  2. CFactorialRunner : This will be a exectuable C target which will use LocalFactorial to find factorial of 5.
  3. SwiftFactorialRunner : This will be a exectuable swift target which will use LocalFactorial to find factorial of 5.

Ofcourse CFactorialRunner and SwiftFactorialRunner can directly use the external Factorial package but we want to keep things interesting.

Structure of the package:

├── Package.swift
└── Sources
    ├── CFactorialRunner
    │   └── main.c
    ├── LocalFactorial
    │   ├── include
    │   │   └── LocalFactorial
    │   │       └── LocalFactorial.h
    │   └── LocalFactorial.c
    └── SwiftFactorialRunner
        └── main.swift

Just to cover all cases, I placed that Localfactorial.h is at include/Localfactorial/Localfactorial.h instead of include/Localfactorial.h which is one of the supported convention.

Package.swift :

import PackageDescription

let package = Package(
  name: "FactorialRunner",
  targets: [
    Target(name: "CFactorialRunner", dependencies: ["LocalFactorial"]),
    Target(name: "SwiftFactorialRunner", dependencies: ["LocalFactorial"]),
    Target(name: "LocalFactorial"),
  ],
  dependencies: [
    .Package(url: "../Factorial", majorVersion: 1),
  ]
)

We want to use LocalFactorial in the exectuables so it is a dependency to CFactorialRunner and SwiftFactorialRunner targets.

LocalFactorial.h:

long local_factorial(int n);

LocalFactorial.c:

#include "include/LocalFactorial/LocalFactorial.h"
#include <Factorial.h>

long local_factorial(int n) {
    return factorial(n);
}

We just use the original factorial method and wrap it with the method local_factorial. Since Factorial is an external dependency it is included using angular brackets <> and not using quotes "". Again we don’t need modulemap because it’ll be generated for us.

main.c :

#include "Localfactorial/Localfactorial.h"
#include <Factorial.h>
#include <stdio.h>

int main() {
    printf("%ld\n", local_factorial(5));
    printf("%ld\n", factorial(5));
}

For C executable we just include the LocalFactorial or Factorial headers and call their methods.

main.swift :

import LocalFactorial
import Factorial

print("\(local_factorial(5))")
print("\(factorial(5))")

For Swift executable we import the packages and done~

$ swift build
Cloning /Users/aciid/mycode/swiftpmProjects/Factorial
Resolved version: 1.0.0
mkdir -p /Users/aciid/mycode/swiftpmProjects/FactorialRunner/.build/debug/CFactorialRunner.build
mkdir -p /Users/aciid/mycode/swiftpmProjects/FactorialRunner/.build/debug/Factorial.build
mkdir -p /Users/aciid/mycode/swiftpmProjects/FactorialRunner/.build/debug/LocalFactorial.build
Compiling Factorial factorial.c
Linking Factorial
Compiling LocalFactorial localfactorial.c
Linking LocalFactorial
Compiling Swift Module 'SwiftFactorialRunner' (1 sources)
Compiling CFactorialRunner main.c
Linking CFactorialRunner
Linking .build/debug/SwiftFactorialRunner

$ .build/debug/SwiftFactorialRunner
120
120

$ .build/debug/CFactorialRunner
120
120

PS : If you work in a company with iOS development, I am looking for a new job.