Skip to content

Commit

Permalink
Merge pull request #1030 from RyanRory/third-party-license
Browse files Browse the repository at this point in the history
Third party licenses
  • Loading branch information
mpretty-cyro authored Oct 10, 2024
2 parents 41a3f27 + 16ba002 commit ae43d9b
Show file tree
Hide file tree
Showing 5 changed files with 1,711 additions and 1,847 deletions.
150 changes: 150 additions & 0 deletions Scripts/GenerateLicenses.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
#!/usr/bin/xcrun --sdk macosx swift

// Copyright © 2024 Rangeproof Pty Ltd. All rights reserved.
//
// stringlint:disable

import Foundation

// Get the Derived Data path and the project's name
let derivedDataPath = getDerivedDataPath() ?? ""
let projectName = ProcessInfo.processInfo.environment["PROJECT_NAME"] ?? ""
let projectPath = ProcessInfo.processInfo.environment["PROJECT_DIR"] ?? FileManager.default.currentDirectoryPath

let packageResolutionFilePath = "\(projectPath)/\(projectName).xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved"
let packageCheckoutsPath = "\(derivedDataPath)/SourcePackages/checkouts/"
let packageArtifactsPath = "\(derivedDataPath)/SourcePackages/artifacts/"

func getDerivedDataPath() -> String? {
// Define the regular expression pattern to extract the DerivedData path
let regexPattern = ".*DerivedData/[^/]*"
guard
let buildDir = ProcessInfo.processInfo.environment["BUILD_DIR"],
let regex = try? NSRegularExpression(pattern: regexPattern)
else { return nil }

let range = NSRange(location: 0, length: buildDir.utf16.count)

// Perform the regex matching
if let match = regex.firstMatch(in: buildDir, options: [], range: range) {
// Extract the matching portion (the DerivedData path)
if let range = Range(match.range, in: buildDir) {
return String(buildDir[range])
}
} else {
print("No DerivedData path found in BUILD_DIR")
}

return nil
}

// Function to list all directories (Swift package checkouts) inside the SourcePackages/checkouts directory
func listDirectories(atPath path: String) -> [String] {
let fileManager = FileManager.default
do {
let items = try fileManager.contentsOfDirectory(atPath: path)
return items.filter { item in
var isDir: ObjCBool = false
let fullPath = path + "/" + item
return fileManager.fileExists(atPath: fullPath, isDirectory: &isDir) && isDir.boolValue
}
} catch {
print("Error reading contents of directory: \(error)")
return []
}
}

// Function to find and read LICENSE files in each package
func findLicenses(in packagesPath: String) -> [(package: String, licenseContent: String)] {
var licenses: [(package: String, licenseContent: String)] = []
let packages: [String] = listDirectories(atPath: packagesPath)

print("\(packages.count) packages found in \(packagesPath)")

packages.forEach { package in
let packagePath = "\(packagesPath)/\(package)"
scanDirectory(atPath: packagePath) { filePath in
if filePath.lowercased().contains("license") || filePath.lowercased().contains("copying") {
if let licenseContent = try? String(contentsOfFile: filePath, encoding: .utf8) {
licenses.append((package, licenseContent))
}
}
}
}

return licenses
}

func findPackageDependencyNames(in resolutionFilePath: String) throws -> Set<String> {
struct ResolvedPackages: Codable {
struct Pin: Codable {
struct State: Codable {
let revision: String
let version: String
}

let identity: String
let kind: String
let location: String
let state: State
}

let originHash: String
let pins: [Pin]
let version: Int
}

do {
let data: Data = try Data(contentsOf: URL(fileURLWithPath: resolutionFilePath))
let resolvedPackages: ResolvedPackages = try JSONDecoder().decode(ResolvedPackages.self, from: data)

print("Found \(resolvedPackages.pins.count) resolved packages.")
return Set(resolvedPackages.pins.map { $0.identity.lowercased() })
}
catch {
print("error: Failed to load list of resolved packages")
throw error
}
}

func scanDirectory(atPath path: String, foundFile: (String) -> Void) {
if let enumerator = FileManager.default.enumerator(atPath: path) {
for case let file as String in enumerator {
let fullPath = "\(path)/\(file)"
if FileManager.default.fileExists(atPath: fullPath, isDirectory: nil) {
foundFile(fullPath)
}
}
}
}

// Write licenses to a plist file
func writePlist(licenses: [(package: String, licenseContent: String)], resolvedPackageNames: Set<String>, outputPath: String) {
var plistArray: [[String: String]] = []
let finalLicenses: [(package: String, licenseContent: String)] = licenses
.filter { resolvedPackageNames.contains($0.package.lowercased()) }
.sorted(by: { $0.package.lowercased() < $1.package.lowercased() })

print("\(finalLicenses.count) being written to plist.")

finalLicenses.forEach { license in
plistArray.append([
"Title": license.package,
"License": license.licenseContent
])
}

let plistData = try! PropertyListSerialization.data(fromPropertyList: plistArray, format: .xml, options: 0)
let plistURL = URL(fileURLWithPath: outputPath)
try? plistData.write(to: plistURL)
}

// Execute the license discovery process
let licenses = findLicenses(in: packageCheckoutsPath) + findLicenses(in: packageArtifactsPath)
let resolvedPackageNames = try findPackageDependencyNames(in: packageResolutionFilePath)

// Specify the path for the output plist
let outputPlistPath = "\(projectPath)/\(projectName)/Meta/Settings.bundle/ThirdPartyLicenses.plist"
writePlist(licenses: licenses, resolvedPackageNames: resolvedPackageNames, outputPath: outputPlistPath)

print("Licenses generated successfully at \(outputPlistPath)")
23 changes: 23 additions & 0 deletions Session.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -1383,6 +1383,7 @@
94367C422C6C828500814252 /* Localizable.xcstrings */ = {isa = PBXFileReference; lastKnownFileType = text.json.xcstrings; path = Localizable.xcstrings; sourceTree = "<group>"; };
943C6D812B75E061004ACE64 /* Message+DisappearingMessages.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Message+DisappearingMessages.swift"; sourceTree = "<group>"; };
943C6D832B86B5F1004ACE64 /* Localization.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Localization.swift; sourceTree = "<group>"; };
9471CAA72CACFB4E00090FB7 /* GenerateLicenses.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GenerateLicenses.swift; sourceTree = "<group>"; };
9473386D2BDF5F3E00B9E169 /* InfoPlist.xcstrings */ = {isa = PBXFileReference; lastKnownFileType = text.json.xcstrings; path = InfoPlist.xcstrings; sourceTree = "<group>"; };
947AD68F2C8968FF000B2730 /* Constants.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Constants.swift; sourceTree = "<group>"; };
94B3DC162AF8592200C88531 /* QuoteView_SwiftUI.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QuoteView_SwiftUI.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -4387,6 +4388,7 @@
FDE72150287E50D50093DF33 /* LintLocalizableStrings.swift */,
FD5CE3442A3C5D96001A6DE3 /* DecryptExportedKey.swift */,
FDEF57642C44B8C200131302 /* ProcessIP2CountryData.swift */,
9471CAA72CACFB4E00090FB7 /* GenerateLicenses.swift */,
);
path = Scripts;
sourceTree = "<group>";
Expand Down Expand Up @@ -4777,6 +4779,7 @@
FD5E93D32C12D3990038C25A /* Add App Group To Build Info Plist */,
FDC498BF2AC1747900EDD897 /* Ensure Localizable.strings included */,
FD0B1FA92CA3805C00F60F46 /* Ensure InfoPlist.xcstrings updated */,
9471CAA62CACFB0600090FB7 /* Generate Licenses Plist */,
);
buildRules = (
);
Expand Down Expand Up @@ -5283,6 +5286,26 @@
shellScript = "\"${SRCROOT}/Scripts/LintLocalizableStrings.swift\" update\n";
showEnvVarsInLog = 0;
};
9471CAA62CACFB0600090FB7 /* Generate Licenses Plist */ = {
isa = PBXShellScriptBuildPhase;
alwaysOutOfDate = 1;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
);
inputPaths = (
);
name = "Generate Licenses Plist";
outputFileListPaths = (
);
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${SRCROOT}/Scripts/GenerateLicenses.swift\"\n";
showEnvVarsInLog = 0;
};
FD5E93D32C12D3990038C25A /* Add App Group To Build Info Plist */ = {
isa = PBXShellScriptBuildPhase;
alwaysOutOfDate = 1;
Expand Down
Loading

0 comments on commit ae43d9b

Please sign in to comment.