Skip to content

Latest commit

 

History

History
116 lines (90 loc) · 5.06 KB

README.md

File metadata and controls

116 lines (90 loc) · 5.06 KB

Build Status codecov Platforms License

Clangler is a Swift package used to parse Clang module map files into an abstract syntax tree (AST) representation. Once parsed, you can inspect or manipulate the nodes in the file, then generate and save a new file reflecting your changes.

Examples

Find the names of all modules in a file:

func findAllTopLevelModuleNames(fileURL: URL) throws -> [String] {
    let moduleMap = try Parser().parseFile(at: fileURL).get()
    return moduleMap.moduleDeclarations.map(\.moduleId.rawValue)
}

Remove an existing umbrella header declaration and replace it with an umbrella directory:

func convertToUmbrellaDirectory(fileURL: URL) throws {
    var moduleMap = try Parser().parseFile(at: fileURL).get()

    // Find the module declaration and the existing umbrella header
    guard var declaration = moduleMap.moduleDeclarations.first?.local,
          let umbrellaHeaderMemberIndex = declaration.members
            .firstIndex(where: { $0.header?.kind == .umbrella })
    else { return }

    // Replace the umbrella header with an umbrella directory
    declaration.members[umbrellaHeaderMemberIndex] = .umbrellaDirectory(
        UmbrellaDirectoryDeclaration(filePath: "Headers")
    )

    // Replace the module declaration with the new one
    moduleMap.moduleDeclarations[0] = .local(declaration)

    // Save the modified file
    let generator = Generator(indentationStyle: .spaces(4))
    let newFileContents = generator.generateFileContents(with: moduleMap)
    try newFileContents.write(to: fileURL, atomically: true, encoding: .utf8)
}

Create a brand new module map from scratch instead of parsing one:

func buildModuleMapFromScratch(moduleName: String, headerPaths: [String]) throws {
    let moduleMap = ModuleMapFile(
        moduleDeclarations: [
            .local(LocalModuleDeclaration(
                explicit: false,
                framework: true,
                moduleId: ModuleId(rawValue: moduleName),
                attributes: [],
                members: headerPaths.map { filePath in
                    .header(HeaderDeclaration(
                        kind: .standard(private: false),
                        filePath: filePath,
                        headerAttributes: []
                    ))
                }
            ))
        ]
    )

    // Save the file
    let generator = Generator(indentationStyle: .spaces(4))
    let fileContents = generator.generateFileContents(with: moduleMap)
    let fileURL = URL(fileURLWithPath: "path/to/\(moduleName)/module.modulemap")
    try fileContents.write(to: fileURL, atomically: true, encoding: .utf8)
}

Discover syntax errors in a file:

func printAllSyntaxErrors(fileURL: URL) throws {
    let result = try Parser().parseFile(at: fileURL)
    guard case .failure(let syntaxErrors) = result else {
        return print("No syntax errors")
    }

    for error in syntaxErrors {
        print("line: \(error.line), column: \(error.column), description: \(error.description)")
    }
}

Notable types

  • Parser — Used to parse the contents of a Clang module map file into an abstract syntax tree (AST) representation
  • Lexer — Scans a module map string and produces tokens in the module map grammar. You will typically not instantiate this type directly as Parser uses it under the hood.
  • Generator — Used to generate module map file contents from an abstract syntax tree (AST) representation. This is the reverse of Parser.
  • ModuleMapFile — The root node in the module map AST. This represents a complete module map file as a collection of declarations.

Installation

In Xcode, you can add this package to your project by selecting File -> Swift Packages -> Add Package Dependency… Enter the Clangler GitHub URL and follow the prompts.

If you use a Package.swift file instead, add the following line inside of your package dependencies array:

.package(url: "https://github.com/daltonclaybrook/Clangler", from: "0.1.0"),

Now add Clangler as a dependency of any relevant targets:

.target(name: "MyApp", dependencies: ["Clangler"]),

License

Clangler is available under the MIT license. See LICENSE.md for more information.