Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merge signal generation and other features #10

Merged
merged 18 commits into from
Jan 8, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
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
120 changes: 120 additions & 0 deletions .github/workflows/build-gtk.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
name: Build gir2swift and build SwiftGtk

# Dependencies of Glib package
env:
MACOS_BREW: ${{ 'glib gobject-introspection pango atk gtk+3 cairo glib-networking gdk-pixbuf' }}
UBUNTU_APT: ${{ 'libpango1.0-dev libglib2.0-dev libgdk-pixbuf2.0-dev gobject-introspection libcairo2-dev libatk1.0-dev glib-networking libgtk-3-dev libgirepository1.0-dev' }}


on:
pull_request:
branches: [ master ]
jobs:
# macOS tasks
build-mac-swift-latest:
runs-on: macos-latest
steps:
- name: Checkout
uses: actions/checkout@v2
- uses: maxim-lobanov/setup-xcode@v1
with:
xcode-version: '12.2'
- name: Print Swift version to confirm
run: swift --version
- name: Fetch dependencies for general repository
run: brew install $MACOS_BREW

- name: Checkout gir2swift
uses: actions/checkout@v2
with:
path: gir2swift
- name: Checkout testing repo
uses: actions/checkout@v2
with:
repository: mikolasstuchlik/SwiftGtk
path: SwiftGtk

- name: Build current gir2swift
run: |
cd gir2swift
./build.sh
echo "GIR2SWIFT_PATH=${PWD}/.build/release/gir2swift" >> $GITHUB_ENV
cd ..

- name: Build gtk
run: |
cd SwiftGtk
swift package update
../gir2swift/gir2swift-generation-driver.sh generate "$PWD" "$GIR2SWIFT_PATH"
swift build -Xswiftc -suppress-warnings `../gir2swift/gir2swift-generation-driver.sh "c-flags" "${PWD}"`
cd ..

- name: Remove unneeded files and archive artifacts
run: |
cd gir2swift
swift package clean
rm -rf .build/repositories
cd ../SwiftGtk
swift package clean
rm -rf .build/repositories
cd ..
- name: 'Upload Artifact'
uses: actions/upload-artifact@v2
with:
name: build-artifact-macos
path: |
gir2swift/
SwiftGtk/
retention-days: 1

# Ubuntu 20.04 tasks
build-ubuntu-20_04-swift-latest:
runs-on: ubuntu-20.04
steps:
- name: Print Swift version to confirm
run: swift --version
- name: Fetch dependencies for general repository
run: sudo apt-get install $UBUNTU_APT

- name: Checkout gir2swift
uses: actions/checkout@v2
with:
path: gir2swift
- name: Checkout testing repo
uses: actions/checkout@v2
with:
repository: mikolasstuchlik/SwiftGtk
path: SwiftGtk

- name: Build current gir2swift
run: |
cd gir2swift
./build.sh
echo "GIR2SWIFT_PATH=${PWD}/.build/release/gir2swift" >> $GITHUB_ENV
cd ..

- name: Build gtk
run: |
cd SwiftGtk
swift package update
../gir2swift/gir2swift-generation-driver.sh generate "$PWD" "$GIR2SWIFT_PATH"
swift build -Xswiftc -suppress-warnings
cd ..

- name: Remove unneeded files and archive artifacts
run: |
cd gir2swift
swift package clean
rm -rf .build/repositories
cd ../SwiftGtk
swift package clean
rm -rf .build/repositories
cd ..
- name: 'Upload Artifact'
uses: actions/upload-artifact@v2
with:
name: build-artifact-20.04-5.3
path: |
gir2swift/
SwiftGtk/
retention-days: 1
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@
/Packages
/Package.resolved
/*.xcodeproj
/.swiftpm
16 changes: 16 additions & 0 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "Launch",
"type": "lldb",
"request": "launch",
"program": "${workspaceFolder}/.build/debug/${workspaceFolderBasename}",
"cwd": "${workspaceFolder}/../test-SwiftGtk",
"args": ["-o", "Sources/Gtk", "-s", "-m", "Gtk-3.0.module", "-p", "/usr/share/gir-1.0/Atk-1.0.gir", "-p", "/usr/share/gir-1.0/cairo-1.0.gir", "-p", "/usr/share/gir-1.0/Gdk-3.0.gir", "-p", "/usr/share/gir-1.0/GdkPixbuf-2.0.gir", "-p", "/usr/share/gir-1.0/Gio-2.0.gir", "-p", "/usr/share/gir-1.0/GLib-2.0.gir", "-p", "/usr/share/gir-1.0/GModule-2.0.gir", "-p", "/usr/share/gir-1.0/GObject-2.0.gir", "-p", "/usr/share/gir-1.0/Pango-1.0.gir", "-p", "/usr/share/gir-1.0/PangoCairo-1.0.gir", "/usr/share/gir-1.0/Gtk-3.0.gir"]
}
]
}
3 changes: 3 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"editor.wordWrap": "on"
}
16 changes: 16 additions & 0 deletions .vscode/tasks.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// .vscode/tasks.json

{
"version" : "2.0.0",
"tasks": [
{
"label": "Swift Build",
"type": "shell",
"command": "swift build -Xswiftc -Xfrontend -Xswiftc -serialize-debugging-options",
"group": {
"kind": "build",
"isDefault": true
}
}
]
}
39 changes: 29 additions & 10 deletions Sources/gir2swift/main.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ var verbose = false

/// Print command line usage and exit
func usage() -> Never {
fputs("Usage: \(CommandLine.arguments[0]) [-v][-s][-m module_boilerplate.swift][-o output_directory]{-p file.gir}[file.gir ...]\n", stderr)
fputs("Usage: \(CommandLine.arguments[0]) [-v][-s][-a][-m module_boilerplate.swift][-o output_directory]{-p file.gir}[file.gir ...]\n", stderr)
exit(EXIT_FAILURE)
}

Expand All @@ -45,11 +45,11 @@ func preload_gir(file: String) {


/// process a GIR file
func process_gir(file: String, boilerPlate modulePrefix: String, to outputDirectory: String? = nil, split singleFilePerClass: Bool = false) {
func process_gir(file: String, boilerPlate modulePrefix: String, to outputDirectory: String? = nil, split singleFilePerClass: Bool = false, generateAll: Bool = false) {
let base = file.baseName
let node = base.stringByRemoving(suffix: ".gir") ?? base
let wlfile = node + ".whitelist"
if let whitelist = String(contentsOfFile: wlfile, quiet: true)?.lines.asSet {
if let whitelist = String(contentsOfFile: wlfile, quiet: true).flatMap({ Set($0.lines) }) {
for name in whitelist {
GIR.knownDataTypes.removeValue(forKey: name)
GIR.knownRecords.removeValue(forKey: name)
Expand All @@ -61,7 +61,7 @@ func process_gir(file: String, boilerPlate modulePrefix: String, to outputDirect
"Notify", "Func", "Marshaller", "Callback"
]
let nsfile = node + ".namespaceReplacements"
if let ns = String(contentsOfFile: nsfile, quiet: true)?.lines.asSet {
if let ns = String(contentsOfFile: nsfile, quiet: true).flatMap({Set($0.lines)}) {
for line in ns {
let keyValues: [Substring]
let tabbedKeyValues: [Substring] = line.split(separator: "\t")
Expand All @@ -76,6 +76,7 @@ func process_gir(file: String, boilerPlate modulePrefix: String, to outputDirect
GIR.namespaceReplacements[key] = value
}
}

load_gir(file) { gir in
processSpecialCases(gir, forFile: node)
let blacklist = GIR.blacklist
Expand Down Expand Up @@ -107,6 +108,7 @@ func process_gir(file: String, boilerPlate modulePrefix: String, to outputDirect
for type in types {
let convert = ptrconvert(type.ptrName)
let code = convert(type)

output += code + "\n\n"
name = type.className
guard let firstChar = name.first else { continue }
Expand Down Expand Up @@ -192,7 +194,19 @@ func process_gir(file: String, boilerPlate modulePrefix: String, to outputDirect
}
background.async(group: queues) {
let convert = swiftCode(gir.functions)
let types = gir.records.filter {!blacklist.contains($0.name)}
var types = gir.records.filter {!blacklist.contains($0.name)}
// If `generate all` option was not passed, the driver will not generate records wich are deemed as private.
// Currently only Private records are ommited. Private record is a record, which has suffic Record and, class with it's name without work "Private" exists and contains only private references to this type or none at all.
// Since not all private attributes of classes are marked as private in .gir, only those records with non-private attributed references will be generated.
if !generateAll {
let classes: [String: GIR.Class] = Dictionary(gir.classes.map { ($0.name, $0) }) { lhs, _ in lhs}
types.removeAll { record in
record.name.hasSuffix("Private") &&
record.name.stringByRemoving(suffix: "Private")
.flatMap { classes[$0] }
.flatMap { $0.fields.allSatisfy { field in field.typeRef.type.name != record.name || field.isPrivate } } == true
}
}
write(types, using: convert)
}
background.async(group: queues) {
Expand All @@ -217,16 +231,17 @@ func process_gir(file: String, boilerPlate modulePrefix: String, to outputDirect
}



/// process blacklist and verbatim constants information
func processSpecialCases(_ gir: GIR, forFile node: String) {
let preamble = node + ".preamble"
gir.preamble = preamble.contents ?? ""
let blacklist = node + ".blacklist"
GIR.blacklist = blacklist.contents?.lines.asSet ?? []
GIR.blacklist = blacklist.contents.flatMap { Set($0.lines) } ?? []
let verbatimConstants = node + ".verbatim"
GIR.verbatimConstants = verbatimConstants.contents?.lines.asSet ?? []
GIR.verbatimConstants = verbatimConstants.contents.flatMap { Set($0.lines) } ?? []
let overrideFile = node + ".override"
GIR.overrides = overrideFile.contents?.lines.asSet ?? []
GIR.overrides = overrideFile.contents.flatMap { Set($0.lines) } ?? []
}

let nTypesPrior = GIR.knownTypes.count
Expand All @@ -239,7 +254,9 @@ var moduleBoilerPlate: String = ""
var outputDirectory: String?
/// `true` to create a single file per type
var singleFilePerClass = false
while let (opt, param) = get_opt("m:o:p:sv") {
/// `true` gir2swift will generate wrappers for all types, despite being private or unreachable
var generateAll = false
while let (opt, param) = get_opt("m:o:p:sv:a") {
switch opt {
case "m":
guard let bpfile = param,
Expand All @@ -255,13 +272,15 @@ while let (opt, param) = get_opt("m:o:p:sv") {
singleFilePerClass = true
case "v":
verbose = true
case "a":
generateAll = true
default:
usage()
}
}

for argument in CommandLine.arguments[Int(optind)..<CommandLine.arguments.count] {
process_gir(file: argument, boilerPlate: moduleBoilerPlate, to: outputDirectory, split: singleFilePerClass)
process_gir(file: argument, boilerPlate: moduleBoilerPlate, to: outputDirectory, split: singleFilePerClass, generateAll: generateAll)
}

if verbose {
Expand Down
27 changes: 0 additions & 27 deletions Sources/libgir2swift/Sequence+Utilities.swift

This file was deleted.

53 changes: 53 additions & 0 deletions Sources/libgir2swift/emmiting/CodeBuilder.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import Foundation


/// Code Builder is a ResultBuilder class which provides easier-to-read way of composing string with indentation and new lines. Element of the DSL generated by this class is `String`. As for now, only if-else statements are available. At the time of implementation, for-in statement was not widely available.
/// - Note: ResultBuilder is merged in Swift 5.4 and available since Swift 5.1
@_functionBuilder
class CodeBuilder {
/// Ignoring sequence is introduec in order to prevent superfulous line breaks in conditions
static let ignoringEspace: String = "<%IGNORED%>"

/// Following static methods are documented in https://github.com/apple/swift-evolution/blob/main/proposals/0289-result-builders.md
static func buildBlock( _ segments: String...) -> String {
segments.filter { $0 != CodeBuilder.ignoringEspace } .joined(separator: "\n")
}

static func buildEither(first: String) -> String { first }
static func buildEither(second: String) -> String { second }

static func buildOptional(_ component: String?) -> String { component ?? CodeBuilder.ignoringEspace }
static func buildIf(_ segment: String?) -> String { buildOptional(segment) }
}

/// Convenience class for using CodeBuilder DSL. This class was introduced to shorten calls. As for now, this class compensates for some missing DSL features like for-in loops.
class Code {
static var defaultCodeIndentation: String = " "

/// Code in builder block of this function will have additional indentation passed in the first argument.
/// - Parameter indentation: Intendation added on top of existing indentation. Default value is value of static property `defaultCodeIndentation`. Pass `nil` or "" to ommit indentation.
static func block(indentation: String? = defaultCodeIndentation, @CodeBuilder builder: ()->String) -> String {
let code = builder()

if let indentation = indentation {
return indentation + (code.replacingOccurrences(of: "\n", with: "\n" + indentation))
}

return builder()
}

/// Strings inside of this builder have all occurances of `\n` removed.
static func line(@CodeBuilder builder: ()->String) -> String {
builder().components(separatedBy: "\n").joined()
}

/// Loop provided as a replacement for missing for-in loop. This function will be removed in the future. For Enumerated variant use `loopEnumerated(over:builder:)`
static func loop<T>(over items: [T], @CodeBuilder builder: (T)->String) -> String {
!items.isEmpty ? items.map(builder).joined(separator: "\n") : CodeBuilder.ignoringEspace
}

/// Loop provided as a replacement for missing for-in loop. Array is returned as enumerated.
static func loopEnumerated<T>(over items: [T], @CodeBuilder builder: (Int, T)->String) -> String {
!items.isEmpty ? items.enumerated().map(builder).joined(separator: "\n") : CodeBuilder.ignoringEspace
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ private let typeNames: Set = reservedTypes.union(reversecast.keys)
/// UnicodeScalars representing whitespaces and newlines
private let wsnlScalars: Set<UnicodeScalar> = [ " ", "\t", "\n"]
/// Set of whitespace and newline ASCII/UTF8 codes
private let wsnl = wsnlScalars.map { UInt8($0.value) }.asSet
private let wsnl = Set(wsnlScalars.map { UInt8($0.value) })

/// Swift keyword for `true` Boolean values
private let trueS = "true"
Expand Down
Loading