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

Support building on Windows. #129

Open
wants to merge 9 commits into
base: master
Choose a base branch
from
Open
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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
# gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore

## Build generated
.build/
build/
DerivedData

Expand Down
15 changes: 15 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
cmake_minimum_required(VERSION 3.19.6)

project(Fuzi LANGUAGES Swift)

set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
set(CMAKE_Swift_MODULE_DIRECTORY ${CMAKE_BINARY_DIR}/swift)

if(CMAKE_SYSTEM_NAME STREQUAL "Windows")
# LibXml2 must be built on Windows. It's preinstalled on MacOS.
add_subdirectory(Vendor)
endif()

add_subdirectory(Sources)
11 changes: 11 additions & 0 deletions Sources/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
add_library(Fuzi
Document.swift
Element.swift
Error.swift
Helpers.swift
Node.swift
NodeSet.swift
Queryable.swift)

target_link_libraries(Fuzi PUBLIC
LibXml2)
12 changes: 5 additions & 7 deletions Sources/Document.swift
Original file line number Diff line number Diff line change
Expand Up @@ -30,15 +30,13 @@ open class XMLDocument {
return ^-^self.cDocument.pointee.version
}()

/// The string encoding for the document. This is NSUTF8StringEncoding if no encoding is set, or it cannot be calculated.
/// The string encoding for the document. This is utf8 if no encoding is set, or it cannot be calculated.
open fileprivate(set) lazy var encoding: String.Encoding = {
if let encodingName = ^-^self.cDocument.pointee.encoding {
let encoding = CFStringConvertIANACharSetNameToEncoding(encodingName as CFString?)
if encoding != kCFStringEncodingInvalidId {
return String.Encoding(rawValue: UInt(CFStringConvertEncodingToNSStringEncoding(encoding)))
}
guard let encodingStr = ^-^self.cDocument.pointee.encoding,
let encoding = String.Encoding(ianaCharsetName: encodingStr) else {
return String.Encoding.utf8
}
return String.Encoding.utf8
return encoding
}()

// MARK: - Accessing the Root Element
Expand Down
2 changes: 1 addition & 1 deletion Sources/Error.swift
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ public enum XMLError: Error {
}
let message = (^-^errorPtr.pointee.message)?.trimmingCharacters(in: CharacterSet.whitespacesAndNewlines)
let code = Int(errorPtr.pointee.code)
xmlResetError(errorPtr)
xmlResetError(UnsafeMutablePointer(mutating: errorPtr))
return .libXMLError(code: code, message: message ?? "")
}
}
45 changes: 45 additions & 0 deletions Sources/Helpers.swift
Original file line number Diff line number Diff line change
Expand Up @@ -146,3 +146,48 @@ internal func cXMLNode(_ node: xmlNodePtr?, matchesTag tag: XMLCharsComparable,
}
return matches
}

internal extension String.Encoding {
// CoreFoundation is not available on Windows, so mimic the functionality
// of CFStringConvertIANACharSetNameToEncoding() and
// CFStringConvertEncodingToNSStringEncoding() here. See the IANA charset
// name registry at https://www.iana.org/assignments/character-sets/character-sets.xhtml
init?(ianaCharsetName name: String) {
switch name.lowercased() {
case "iso-2022-jp":
self = .iso2022JP
case "iso-8859-1":
self = .isoLatin1
case "iso-8859-2":
self = .isoLatin2
case "unicode-1-1", "iso-10646-ucs-2", "utf-16":
self = .utf16
case "utf-8":
self = .utf8
case "utf-16be":
self = .utf16BigEndian
case "utf-16le":
self = .utf16LittleEndian
case "utf-32":
self = .utf32
case "utf-32be":
self = .utf32BigEndian
case "utf-32le":
self = .utf32LittleEndian
case "windows-1250":
self = .windowsCP1250
case "windows-1251":
self = .windowsCP1251
case "windows-1252":
self = .windowsCP1252
case "windows-1253":
self = .windowsCP1253
case "windows-1254":
self = .windowsCP1254
case "us-ascii":
self = .ascii
default:
return nil
}
}
}
17 changes: 14 additions & 3 deletions Sources/Node.swift
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,15 @@ extension XMLNodeType {
/// XInclude End
public static var XIncludeEnd: xmlElementType { return XML_XINCLUDE_END }
/// DocbDocument
public static var DocbDocument: xmlElementType { return XML_DOCB_DOCUMENT_NODE }
public static var DocbDocument: xmlElementType {
#if os(Windows)
// The DOCB type is not part of the enum in libxml2, it's a #define. On
// Windows it gets imported as an incompatible type, so explicitly cast it.
return xmlElementType(XML_DOCB_DOCUMENT_NODE)
#else
return XML_DOCB_DOCUMENT_NODE
#endif
}
}

infix operator ~=
Expand Down Expand Up @@ -97,7 +105,8 @@ open class XMLNode {

/// The element's line number.
open fileprivate(set) lazy var lineNumber: Int = {
return xmlGetLineNo(self.cNode)
// On Windows `long` is a 32 bit value so explicitly cast to Int.
return Int(xmlGetLineNo(self.cNode))
}()

// MARK: - Accessing Parent and Sibling Elements
Expand All @@ -119,7 +128,9 @@ open class XMLNode {
// MARK: - Accessing Contents
/// Whether this is a HTML node
open var isHTML: Bool {
return UInt32(self.cNode.pointee.doc.pointee.properties) & XML_DOC_HTML.rawValue == XML_DOC_HTML.rawValue
// On Windows the bitset is imported as an incompatible type, so explicitly cast it.
let xmlDocHtmlBit = UInt32(XML_DOC_HTML.rawValue)
return UInt32(self.cNode.pointee.doc.pointee.properties) & xmlDocHtmlBit == xmlDocHtmlBit
}

/// A string representation of the element's value.
Expand Down
30 changes: 30 additions & 0 deletions Vendor/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
include(FetchContent)

# Turn off noisy deprecation warnings from Microsoft.
add_compile_definitions(_CRT_SECURE_NO_WARNINGS _CRT_NONSTDC_NO_WARNINGS)

# Set libxml2 build options.
set(LIBXML2_WITH_ICONV OFF)
set(LIBXML2_WITH_PYTHON OFF)
set(BUILD_SHARED_LIBS OFF)

# Fetch and build libxml2.
FetchContent_Declare(LibXml2
GIT_REPOSITORY "https://github.com/GNOME/libxml2.git"
GIT_TAG "v2.13.3")
FetchContent_MakeAvailable(LibXml2)

# libxml2 needs a modulemap to be usable from Swift. Glob the headers
# and write one to their headers directory.
file(GLOB LIBXML2_HEADERS "${libxml2_SOURCE_DIR}/include/libxml/*.h")
set(MODULE_CONTENT "module libxml2 {\n")
foreach(HEADER ${LIBXML2_HEADERS})
get_filename_component(HEADER_NAME ${HEADER} NAME)
string(APPEND MODULE_CONTENT " header \"${HEADER_NAME}\"\n")
endforeach()
string(APPEND MODULE_CONTENT " export *\n}")
file(CONFIGURE OUTPUT ${libxml2_SOURCE_DIR}/include/libxml/module.modulemap
CONTENT "${MODULE_CONTENT}")

# Make libxml2 available in the proper case.
add_library(libxml2 ALIAS LibXml2)