Skip to content

Commit

Permalink
Merge pull request #2502 from blender/fix/issue-2500
Browse files Browse the repository at this point in the history
Use `objdump` to determine whether a binary blob from a compressed archive should be handled by Carthage.
  • Loading branch information
jdhealy committed Jun 29, 2018
2 parents 90c9718 + 98aee40 commit 0ec71e1
Show file tree
Hide file tree
Showing 8 changed files with 313 additions and 37 deletions.
8 changes: 8 additions & 0 deletions Carthage.xcodeproj/project.pbxproj
Expand Up @@ -16,6 +16,8 @@
3B19041E1E4CFE4A00A866AD /* FakeOldObjc.framework in Resources */ = {isa = PBXBuildFile; fileRef = 3B19041C1E4CFE3900A866AD /* FakeOldObjc.framework */; };
3B19041F1E4CFE4A00A866AD /* FakeOldSwift.framework in Resources */ = {isa = PBXBuildFile; fileRef = 3B19041D1E4CFE3900A866AD /* FakeOldSwift.framework */; };
3BE5BD9A1E4E5A7B00DDDC45 /* SwiftVersionError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3BE5BD981E4E58ED00DDDC45 /* SwiftVersionError.swift */; };
4396DC8F20E4E8A8002EE967 /* MachHeaderSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4396DC8E20E4E8A8002EE967 /* MachHeaderSpec.swift */; };
43D7809520E4324E004BCDD6 /* MachHeader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4396DC8C20E3EF5C002EE967 /* MachHeader.swift */; };
5482DAF11A3849D700197FB8 /* CopyFrameworks.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5482DAF01A3849D700197FB8 /* CopyFrameworks.swift */; };
54911EF31A1D34EC00FFAE5F /* Version.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54911EF21A1D34EC00FFAE5F /* Version.swift */; };
549B47B11A4F1A34002498C7 /* ProjectSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 549B47AF1A4F17FF002498C7 /* ProjectSpec.swift */; };
Expand Down Expand Up @@ -184,6 +186,8 @@
3B19041C1E4CFE3900A866AD /* FakeOldObjc.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = FakeOldObjc.framework; sourceTree = "<group>"; };
3B19041D1E4CFE3900A866AD /* FakeOldSwift.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = FakeOldSwift.framework; sourceTree = "<group>"; };
3BE5BD981E4E58ED00DDDC45 /* SwiftVersionError.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SwiftVersionError.swift; sourceTree = "<group>"; };
4396DC8C20E3EF5C002EE967 /* MachHeader.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MachHeader.swift; sourceTree = "<group>"; };
4396DC8E20E4E8A8002EE967 /* MachHeaderSpec.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MachHeaderSpec.swift; sourceTree = "<group>"; };
5482DAF01A3849D700197FB8 /* CopyFrameworks.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CopyFrameworks.swift; sourceTree = "<group>"; };
54911EF21A1D34EC00FFAE5F /* Version.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Version.swift; sourceTree = "<group>"; };
5499CA961A2394B700783309 /* Components.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Components.plist; sourceTree = "<group>"; };
Expand Down Expand Up @@ -534,6 +538,7 @@
3BE5BD981E4E58ED00DDDC45 /* SwiftVersionError.swift */,
D0DE893F1A0F2CB00030A3EC /* Version.swift */,
F1CB3CFB1D63D05D00C9EB99 /* VersionFile.swift */,
4396DC8C20E3EF5C002EE967 /* MachHeader.swift */,
BE0292481E403355004FB579 /* XCDBLDExtensions.swift */,
D01F8A5319EA2F1700643E7C /* Xcode.swift */,
D0D1216F19E87B05005E4BAA /* Supporting Files */,
Expand Down Expand Up @@ -576,6 +581,7 @@
D0DB09A319EA354200234B16 /* XcodeSpec.swift */,
A940B2F320C033C9001C4C59 /* CartfileCommentsSpec.swift */,
21F11B461FE6787F009FB783 /* DB.swift */,
4396DC8E20E4E8A8002EE967 /* MachHeaderSpec.swift */,
D0D1217C19E87B05005E4BAA /* Supporting Files */,
);
name = CarthageKitTests;
Expand Down Expand Up @@ -847,6 +853,7 @@
D074EDC51A049283001DE082 /* FrameworkExtensions.swift in Sources */,
CDE559291E12263A00ED7F5F /* BuildSettings.swift in Sources */,
88ED56D619ECE34900CBF5C4 /* Git.swift in Sources */,
43D7809520E4324E004BCDD6 /* MachHeader.swift in Sources */,
D0D1219219E88B8F005E4BAA /* GitHub.swift in Sources */,
CD28C99D1E11846200322AF7 /* ProductType.swift in Sources */,
CD43D9DA1F41640E00CD60F6 /* CarthageKitVersion.swift in Sources */,
Expand Down Expand Up @@ -886,6 +893,7 @@
BFFA7A631E3AAFD200CB95A7 /* BinaryProjectSpec.swift in Sources */,
D0C6E5741A57040B00A5E3E7 /* ArchiveSpec.swift in Sources */,
549B47B11A4F1A34002498C7 /* ProjectSpec.swift in Sources */,
4396DC8F20E4E8A8002EE967 /* MachHeaderSpec.swift in Sources */,
D01D82DD1A10B01D00F0DD94 /* ResolverSpec.swift in Sources */,
BF3199C01E32E078007DC0D1 /* DependencySpec.swift in Sources */,
A940B2F520C0363F001C4C59 /* CartfileCommentsSpec.swift in Sources */,
Expand Down
6 changes: 6 additions & 0 deletions Source/CarthageKit/FrameworkExtensions.swift
Expand Up @@ -19,6 +19,12 @@ extension String {
}
}

/// Strips off a prefix string, if present.
internal func stripping(prefix: String) -> String {
guard hasPrefix(prefix) else { return self }
return String(self.dropFirst(prefix.count))
}

/// Strips off a trailing string, if present.
internal func stripping(suffix: String) -> String {
if hasSuffix(suffix) {
Expand Down
146 changes: 146 additions & 0 deletions Source/CarthageKit/MachHeader.swift
@@ -0,0 +1,146 @@
import Foundation
import MachO.loader
import ReactiveTask
import ReactiveSwift
import Result

/// Represents a Mach header
///
/// Provides a unified structure for
/// `MachO.loader.mach_header` and `MachO.loader.mach_header_64`

struct MachHeader {

enum Endianness {
case little
case big
}

let magic: UInt32
let cpuType: cpu_type_t
let cpuSubtype: cpu_type_t
let fileType: UInt32
let ncmds: UInt32
let sizeofcmds: UInt32
let flags: UInt32
let reserved: UInt32?

var is64BitHeader: Bool {

return magic == MH_MAGIC_64 || magic == MH_CIGAM_64
}

var is32BitHeader: Bool {

return !is64BitHeader
}

var endianess: Endianness {

return magic == MH_CIGAM_64 || magic == MH_CIGAM ? .big : .little
}
}

extension MachHeader {

static let carthageSupportedFileTypes: Set<UInt32> = {
return Set([
MH_OBJECT, // Carthage accepts static libraries
MH_BUNDLE, // Bundles https://github.com/ResearchKit/ResearchKit/blob/1.3.0/ResearchKit/Info.plist#L15-L16
MH_DYLIB, // or dynamic shared libraries
].map { UInt32($0) }
)
}()
}

extension MachHeader {

/// Reads the Mach headers from a Mach-O file.
/// - Parameter url: The url of the Mach-O file
/// - Remark: Uses `objdump` to read the header and parse the output.
/// The output is composed of one or more sets of lines like the following:
///
/// Mach header
/// magic cputype cpusubtype caps filetype ncmds sizeofcmds flags
/// 0xfeedfacf 16777223 3 0x00 1 8 1720 0x00002000
///
/// - See Also: [LLVM MachODump.cpp](https://llvm.org/viewvc/llvm-project/llvm/trunk/tools/llvm-objdump/MachODump.cpp?view=markup&pathrev=225383###see%C2%B7line%C2%B72745)

static func headers(forMachOFileAtUrl url: URL) -> SignalProducer<MachHeader, CarthageError> {

// This is the command `otool -h` actually invokes
let task = Task("/usr/bin/xcrun", arguments: [
"objdump",
"-macho",
"-private-header",
"-non-verbose",
url.resolvingSymlinksInPath().path
]
)

return task.launch(standardInput: nil)
.ignoreTaskData()
.map { String(data: $0, encoding: .utf8) ?? "" }
.filter { !$0.isEmpty }
.flatMap(.merge) { (output: String) -> SignalProducer<(String, String), NoError> in
output.linesProducer.combinePrevious()
}.filterMap { (previousLine, currentLine) -> MachHeader? in

let previousLineComponents = previousLine
.components(separatedBy: CharacterSet.whitespaces)
.filter { !$0.isEmpty }
let currentLineComponents = currentLine
.components(separatedBy: CharacterSet.whitespaces)
.filter { !$0.isEmpty }

let strippedComponents = currentLineComponents
.map { $0.stripping(prefix: "0x")}

let magicIdentifiers = [
MH_MAGIC_64,
MH_CIGAM_64,
MH_MAGIC,
MH_CIGAM,
].lazy

guard previousLineComponents == [
"magic",
"cputype",
"cpusubtype",
"caps",
"filetype",
"ncmds",
"sizeofcmds",
"flags"
]
, !strippedComponents.isEmpty
, let magic = UInt32(strippedComponents.first!, radix:16)
, magicIdentifiers.first(where: { $0 == magic }) != nil else {
return nil
}

guard
let cpuType = cpu_type_t(strippedComponents[1], radix: 10),
let cpuSubtype = cpu_subtype_t(strippedComponents[2], radix: 10),
let fileType = UInt32(strippedComponents[4], radix: 10),
let ncmds = UInt32(strippedComponents[5], radix: 10),
let sizeofcmds = UInt32(strippedComponents[6], radix: 10),
let flags = UInt32(strippedComponents[7], radix: 16)
else {
return nil
}

return MachHeader(
magic: magic,
cpuType: cpuType,
cpuSubtype: cpuSubtype,
fileType: fileType,
ncmds: ncmds,
sizeofcmds: sizeofcmds,
flags: flags,
reserved: nil
)
}
.mapError(CarthageError.taskError)
}
}
21 changes: 16 additions & 5 deletions Source/CarthageKit/Project.swift
Expand Up @@ -1282,23 +1282,34 @@ internal func frameworksInDirectory(_ directoryURL: URL) -> SignalProducer<URL,
return filesInDirectory(directoryURL, kUTTypeFramework as String)
.filter { !$0.pathComponents.contains("__MACOSX") }
.filter { url in

// Skip nested frameworks
let frameworksInURL = url.pathComponents.filter { pathComponent in
return (pathComponent as NSString).pathExtension == "framework"
}
return frameworksInURL.count == 1
}.filter { url in

let packageType: PackageType? = Bundle(url: url)?.packageType
// For reasons of speed and the fact that CLI-output structures can change,
// first try the safer method of reading the ‘Info.plist’ from the Framework’s bundle.
let bundle = Bundle(url: url)
let packageType: PackageType? = bundle?.packageType

switch packageType {
case .framework?, .bundle?:
return true
default:
return false
// In case no Info.plist exists check the Mach-O fileType
guard let executableURL = bundle?.executableURL else {
return false
}

return MachHeader.headers(forMachOFileAtUrl: executableURL)
.filter { MachHeader.carthageSupportedFileTypes.contains($0.fileType) }
.reduce(into: Set<UInt32>()) { $0.insert($1.fileType); return }
.map { $0.count == 1 }
.single()?
.value ?? false
}
}
}
}

/// Sends the URL to each dSYM found in the given directory
Expand Down

0 comments on commit 0ec71e1

Please sign in to comment.