diff --git a/Plugins/JExtractSwiftCommandPlugin/JExtractSwiftCommandPlugin.swift b/Plugins/JExtractSwiftCommandPlugin/JExtractSwiftCommandPlugin.swift index 3ea89886..f97feab6 100644 --- a/Plugins/JExtractSwiftCommandPlugin/JExtractSwiftCommandPlugin.swift +++ b/Plugins/JExtractSwiftCommandPlugin/JExtractSwiftCommandPlugin.swift @@ -72,7 +72,7 @@ final class JExtractSwiftCommandPlugin: SwiftJavaPluginProtocol, BuildToolPlugin var arguments: [String] = [ "--input-swift", sourceDir, - "--module-name", sourceModule.name, + "--swift-module", sourceModule.name, "--output-java", context.outputJavaDirectory.path(percentEncoded: false), "--output-swift", context.outputSwiftDirectory.path(percentEncoded: false), // TODO: "--build-cache-directory", ... diff --git a/Plugins/JExtractSwiftPlugin/JExtractSwiftPlugin.swift b/Plugins/JExtractSwiftPlugin/JExtractSwiftPlugin.swift index a2cda352..4b52df26 100644 --- a/Plugins/JExtractSwiftPlugin/JExtractSwiftPlugin.swift +++ b/Plugins/JExtractSwiftPlugin/JExtractSwiftPlugin.swift @@ -55,7 +55,7 @@ struct JExtractSwiftBuildToolPlugin: SwiftJavaPluginProtocol, BuildToolPlugin { var arguments: [String] = [ "--input-swift", sourceDir, - "--module-name", sourceModule.name, + "--swift-module", sourceModule.name, "--output-java", outputJavaDirectory.path(percentEncoded: false), "--output-swift", outputSwiftDirectory.path(percentEncoded: false), // TODO: "--build-cache-directory", ... diff --git a/Plugins/PluginsShared/PluginUtils.swift b/Plugins/PluginsShared/PluginUtils.swift index 691d3375..863cf99c 100644 --- a/Plugins/PluginsShared/PluginUtils.swift +++ b/Plugins/PluginsShared/PluginUtils.swift @@ -72,8 +72,8 @@ extension PluginContext { .appending(path: "Sources") } - func cachedClasspathFile(moduleName: String) -> URL { + func cachedClasspathFile(swiftModule: String) -> URL { self.pluginWorkDirectoryURL - .appending(path: "\(moduleName)", directoryHint: .notDirectory) + .appending(path: "\(swiftModule)", directoryHint: .notDirectory) } } diff --git a/Plugins/SwiftJavaPlugin/SwiftJavaPlugin.swift b/Plugins/SwiftJavaPlugin/SwiftJavaPlugin.swift index 77d7058d..effdcc53 100644 --- a/Plugins/SwiftJavaPlugin/SwiftJavaPlugin.swift +++ b/Plugins/SwiftJavaPlugin/SwiftJavaPlugin.swift @@ -166,8 +166,9 @@ struct SwiftJavaBuildToolPlugin: SwiftJavaPluginProtocol, BuildToolPlugin { displayName: displayName, executable: executable, arguments: [ + // FIXME: change to 'resolve' subcommand "--fetch", configFile.path(percentEncoded: false), - "--module-name", sourceModule.name, + "--swift-module", sourceModule.name, "--output-directory", outputDirectory(context: context, generated: false).path(percentEncoded: false) ], environment: [:], @@ -180,21 +181,21 @@ struct SwiftJavaBuildToolPlugin: SwiftJavaPluginProtocol, BuildToolPlugin { } if !outputSwiftFiles.isEmpty { + let displayName = "Wrapping \(classes.count) Java classes in Swift target '\(sourceModule.name)'" + log("Prepared: \(displayName)") commands += [ .buildCommand( - displayName: "Wrapping \(classes.count) Java classes in Swift target '\(sourceModule.name)'", + displayName: displayName, executable: executable, arguments: arguments, - inputFiles: compiledClassFiles + fetchDependenciesOutputFiles + [ - configFile - ], + inputFiles: compiledClassFiles + fetchDependenciesOutputFiles + [ configFile ], outputFiles: outputSwiftFiles ) ] } else { log("No Swift output files, skip wrapping") } - + return commands } } @@ -202,7 +203,7 @@ struct SwiftJavaBuildToolPlugin: SwiftJavaPluginProtocol, BuildToolPlugin { extension SwiftJavaBuildToolPlugin { func argumentsModuleName(sourceModule: Target) -> [String] { return [ - "--module-name", sourceModule.name + "--swift-module", sourceModule.name ] } diff --git a/Samples/JavaDependencySampleApp/Sources/Test/swift-java.config b/Samples/JavaDependencySampleApp/Sources/Test/swift-java.config new file mode 100644 index 00000000..50fd7337 --- /dev/null +++ b/Samples/JavaDependencySampleApp/Sources/Test/swift-java.config @@ -0,0 +1,429 @@ +{ + "classes" : { + "org.apache.commons.codec.BinaryDecoder" : "BinaryDecoder", + "org.apache.commons.codec.BinaryEncoder" : "BinaryEncoder", + "org.apache.commons.codec.CharEncoding" : "CharEncoding", + "org.apache.commons.codec.Charsets" : "Charsets", + "org.apache.commons.codec.CodecPolicy" : "CodecPolicy", + "org.apache.commons.codec.Decoder" : "Decoder", + "org.apache.commons.codec.DecoderException" : "DecoderException", + "org.apache.commons.codec.Encoder" : "Encoder", + "org.apache.commons.codec.EncoderException" : "EncoderException", + "org.apache.commons.codec.Resources" : "Resources", + "org.apache.commons.codec.StringDecoder" : "StringDecoder", + "org.apache.commons.codec.StringEncoder" : "StringEncoder", + "org.apache.commons.codec.StringEncoderComparator" : "StringEncoderComparator", + "org.apache.commons.codec.binary.Base16" : "Base16", + "org.apache.commons.codec.binary.Base16InputStream" : "Base16InputStream", + "org.apache.commons.codec.binary.Base16OutputStream" : "Base16OutputStream", + "org.apache.commons.codec.binary.Base32" : "Base32", + "org.apache.commons.codec.binary.Base32$Builder" : "Base32.Builder", + "org.apache.commons.codec.binary.Base32InputStream" : "Base32InputStream", + "org.apache.commons.codec.binary.Base32OutputStream" : "Base32OutputStream", + "org.apache.commons.codec.binary.Base64" : "Base64", + "org.apache.commons.codec.binary.Base64$Builder" : "Base64.Builder", + "org.apache.commons.codec.binary.Base64InputStream" : "Base64InputStream", + "org.apache.commons.codec.binary.Base64OutputStream" : "Base64OutputStream", + "org.apache.commons.codec.binary.BaseNCodec" : "BaseNCodec", + "org.apache.commons.codec.binary.BaseNCodec$AbstractBuilder" : "BaseNCodec.AbstractBuilder", + "org.apache.commons.codec.binary.BaseNCodec$Context" : "BaseNCodec.Context", + "org.apache.commons.codec.binary.BaseNCodecInputStream" : "BaseNCodecInputStream", + "org.apache.commons.codec.binary.BaseNCodecOutputStream" : "BaseNCodecOutputStream", + "org.apache.commons.codec.binary.BinaryCodec" : "BinaryCodec", + "org.apache.commons.codec.binary.CharSequenceUtils" : "CharSequenceUtils", + "org.apache.commons.codec.binary.Hex" : "Hex", + "org.apache.commons.codec.binary.StringUtils" : "StringUtils", + "org.apache.commons.codec.cli.Digest" : "Digest", + "org.apache.commons.codec.digest.B64" : "B64", + "org.apache.commons.codec.digest.Blake3" : "Blake3", + "org.apache.commons.codec.digest.Blake3$ChunkState" : "Blake3.ChunkState", + "org.apache.commons.codec.digest.Blake3$EngineState" : "Blake3.EngineState", + "org.apache.commons.codec.digest.Blake3$Output" : "Blake3.Output", + "org.apache.commons.codec.digest.Crypt" : "Crypt", + "org.apache.commons.codec.digest.DigestUtils" : "DigestUtils", + "org.apache.commons.codec.digest.HmacAlgorithms" : "HmacAlgorithms", + "org.apache.commons.codec.digest.HmacUtils" : "HmacUtils", + "org.apache.commons.codec.digest.Md5Crypt" : "Md5Crypt", + "org.apache.commons.codec.digest.MessageDigestAlgorithms" : "MessageDigestAlgorithms", + "org.apache.commons.codec.digest.MurmurHash2" : "MurmurHash2", + "org.apache.commons.codec.digest.MurmurHash3" : "MurmurHash3", + "org.apache.commons.codec.digest.MurmurHash3$IncrementalHash32" : "MurmurHash3.IncrementalHash32", + "org.apache.commons.codec.digest.MurmurHash3$IncrementalHash32x86" : "MurmurHash3.IncrementalHash32x86", + "org.apache.commons.codec.digest.PureJavaCrc32" : "PureJavaCrc32", + "org.apache.commons.codec.digest.PureJavaCrc32C" : "PureJavaCrc32C", + "org.apache.commons.codec.digest.Sha2Crypt" : "Sha2Crypt", + "org.apache.commons.codec.digest.UnixCrypt" : "UnixCrypt", + "org.apache.commons.codec.digest.XXHash32" : "XXHash32", + "org.apache.commons.codec.language.AbstractCaverphone" : "AbstractCaverphone", + "org.apache.commons.codec.language.Caverphone" : "Caverphone", + "org.apache.commons.codec.language.Caverphone1" : "Caverphone1", + "org.apache.commons.codec.language.Caverphone2" : "Caverphone2", + "org.apache.commons.codec.language.ColognePhonetic" : "ColognePhonetic", + "org.apache.commons.codec.language.ColognePhonetic$CologneBuffer" : "ColognePhonetic.CologneBuffer", + "org.apache.commons.codec.language.ColognePhonetic$CologneInputBuffer" : "ColognePhonetic.CologneInputBuffer", + "org.apache.commons.codec.language.ColognePhonetic$CologneOutputBuffer" : "ColognePhonetic.CologneOutputBuffer", + "org.apache.commons.codec.language.DaitchMokotoffSoundex" : "DaitchMokotoffSoundex", + "org.apache.commons.codec.language.DaitchMokotoffSoundex$Branch" : "DaitchMokotoffSoundex.Branch", + "org.apache.commons.codec.language.DaitchMokotoffSoundex$Rule" : "DaitchMokotoffSoundex.Rule", + "org.apache.commons.codec.language.DoubleMetaphone" : "DoubleMetaphone", + "org.apache.commons.codec.language.DoubleMetaphone$DoubleMetaphoneResult" : "DoubleMetaphone.DoubleMetaphoneResult", + "org.apache.commons.codec.language.MatchRatingApproachEncoder" : "MatchRatingApproachEncoder", + "org.apache.commons.codec.language.Metaphone" : "Metaphone", + "org.apache.commons.codec.language.Nysiis" : "Nysiis", + "org.apache.commons.codec.language.RefinedSoundex" : "RefinedSoundex", + "org.apache.commons.codec.language.Soundex" : "Soundex", + "org.apache.commons.codec.language.SoundexUtils" : "SoundexUtils", + "org.apache.commons.codec.language.bm.BeiderMorseEncoder" : "BeiderMorseEncoder", + "org.apache.commons.codec.language.bm.Lang" : "Lang", + "org.apache.commons.codec.language.bm.Lang$LangRule" : "Lang.LangRule", + "org.apache.commons.codec.language.bm.Languages" : "Languages", + "org.apache.commons.codec.language.bm.Languages$LanguageSet" : "Languages.LanguageSet", + "org.apache.commons.codec.language.bm.Languages$SomeLanguages" : "Languages.SomeLanguages", + "org.apache.commons.codec.language.bm.NameType" : "NameType", + "org.apache.commons.codec.language.bm.PhoneticEngine" : "PhoneticEngine", + "org.apache.commons.codec.language.bm.PhoneticEngine$PhonemeBuilder" : "PhoneticEngine.PhonemeBuilder", + "org.apache.commons.codec.language.bm.PhoneticEngine$RulesApplication" : "PhoneticEngine.RulesApplication", + "org.apache.commons.codec.language.bm.ResourceConstants" : "ResourceConstants", + "org.apache.commons.codec.language.bm.Rule" : "Rule", + "org.apache.commons.codec.language.bm.Rule$Phoneme" : "Rule.Phoneme", + "org.apache.commons.codec.language.bm.Rule$PhonemeExpr" : "Rule.PhonemeExpr", + "org.apache.commons.codec.language.bm.Rule$PhonemeList" : "Rule.PhonemeList", + "org.apache.commons.codec.language.bm.Rule$RPattern" : "Rule.RPattern", + "org.apache.commons.codec.language.bm.RuleType" : "RuleType", + "org.apache.commons.codec.net.BCodec" : "BCodec", + "org.apache.commons.codec.net.PercentCodec" : "PercentCodec", + "org.apache.commons.codec.net.QCodec" : "QCodec", + "org.apache.commons.codec.net.QuotedPrintableCodec" : "QuotedPrintableCodec", + "org.apache.commons.codec.net.RFC1522Codec" : "RFC1522Codec", + "org.apache.commons.codec.net.URLCodec" : "URLCodec", + "org.apache.commons.codec.net.Utils" : "Utils", + "org.apache.commons.csv.CSVException" : "CSVException", + "org.apache.commons.csv.CSVFormat" : "CSVFormat", + "org.apache.commons.csv.CSVFormat$Builder" : "CSVFormat.Builder", + "org.apache.commons.csv.CSVFormat$Predefined" : "CSVFormat.Predefined", + "org.apache.commons.csv.CSVParser" : "CSVParser", + "org.apache.commons.csv.CSVParser$CSVRecordIterator" : "CSVParser.CSVRecordIterator", + "org.apache.commons.csv.CSVParser$Headers" : "CSVParser.Headers", + "org.apache.commons.csv.CSVPrinter" : "CSVPrinter", + "org.apache.commons.csv.CSVRecord" : "CSVRecord", + "org.apache.commons.csv.Constants" : "Constants", + "org.apache.commons.csv.DuplicateHeaderMode" : "DuplicateHeaderMode", + "org.apache.commons.csv.ExtendedBufferedReader" : "ExtendedBufferedReader", + "org.apache.commons.csv.Lexer" : "Lexer", + "org.apache.commons.csv.QuoteMode" : "QuoteMode", + "org.apache.commons.csv.Token" : "Token", + "org.apache.commons.csv.Token$Type" : "Token.Type", + "org.apache.commons.io.ByteOrderMark" : "ByteOrderMark", + "org.apache.commons.io.ByteOrderParser" : "ByteOrderParser", + "org.apache.commons.io.Charsets" : "Charsets", + "org.apache.commons.io.CloseableURLConnection" : "CloseableURLConnection", + "org.apache.commons.io.CopyUtils" : "CopyUtils", + "org.apache.commons.io.DirectoryWalker" : "DirectoryWalker", + "org.apache.commons.io.DirectoryWalker$CancelException" : "DirectoryWalker.CancelException", + "org.apache.commons.io.EndianUtils" : "EndianUtils", + "org.apache.commons.io.FileCleaner" : "FileCleaner", + "org.apache.commons.io.FileCleaningTracker" : "FileCleaningTracker", + "org.apache.commons.io.FileCleaningTracker$Reaper" : "FileCleaningTracker.Reaper", + "org.apache.commons.io.FileCleaningTracker$Tracker" : "FileCleaningTracker.Tracker", + "org.apache.commons.io.FileDeleteStrategy" : "FileDeleteStrategy", + "org.apache.commons.io.FileDeleteStrategy$ForceFileDeleteStrategy" : "FileDeleteStrategy.ForceFileDeleteStrategy", + "org.apache.commons.io.FileExistsException" : "FileExistsException", + "org.apache.commons.io.FileSystem" : "FileSystem", + "org.apache.commons.io.FileSystemUtils" : "FileSystemUtils", + "org.apache.commons.io.FileUtils" : "FileUtils", + "org.apache.commons.io.FilenameUtils" : "FilenameUtils", + "org.apache.commons.io.HexDump" : "HexDump", + "org.apache.commons.io.IO" : "IO", + "org.apache.commons.io.IOCase" : "IOCase", + "org.apache.commons.io.IOExceptionList" : "IOExceptionList", + "org.apache.commons.io.IOExceptionWithCause" : "IOExceptionWithCause", + "org.apache.commons.io.IOIndexedException" : "IOIndexedException", + "org.apache.commons.io.IOUtils" : "IOUtils", + "org.apache.commons.io.LineIterator" : "LineIterator", + "org.apache.commons.io.RandomAccessFileMode" : "RandomAccessFileMode", + "org.apache.commons.io.RandomAccessFiles" : "RandomAccessFiles", + "org.apache.commons.io.StandardLineSeparator" : "StandardLineSeparator", + "org.apache.commons.io.StreamIterator" : "StreamIterator", + "org.apache.commons.io.TaggedIOException" : "TaggedIOException", + "org.apache.commons.io.ThreadMonitor" : "ThreadMonitor", + "org.apache.commons.io.ThreadUtils" : "ThreadUtils", + "org.apache.commons.io.UncheckedIOExceptions" : "UncheckedIOExceptions", + "org.apache.commons.io.build.AbstractOrigin" : "AbstractOrigin", + "org.apache.commons.io.build.AbstractOrigin$ByteArrayOrigin" : "AbstractOrigin.ByteArrayOrigin", + "org.apache.commons.io.build.AbstractOrigin$CharSequenceOrigin" : "AbstractOrigin.CharSequenceOrigin", + "org.apache.commons.io.build.AbstractOrigin$FileOrigin" : "AbstractOrigin.FileOrigin", + "org.apache.commons.io.build.AbstractOrigin$InputStreamOrigin" : "AbstractOrigin.InputStreamOrigin", + "org.apache.commons.io.build.AbstractOrigin$OutputStreamOrigin" : "AbstractOrigin.OutputStreamOrigin", + "org.apache.commons.io.build.AbstractOrigin$PathOrigin" : "AbstractOrigin.PathOrigin", + "org.apache.commons.io.build.AbstractOrigin$ReaderOrigin" : "AbstractOrigin.ReaderOrigin", + "org.apache.commons.io.build.AbstractOrigin$URIOrigin" : "AbstractOrigin.URIOrigin", + "org.apache.commons.io.build.AbstractOrigin$WriterOrigin" : "AbstractOrigin.WriterOrigin", + "org.apache.commons.io.build.AbstractOriginSupplier" : "AbstractOriginSupplier", + "org.apache.commons.io.build.AbstractStreamBuilder" : "AbstractStreamBuilder", + "org.apache.commons.io.build.AbstractSupplier" : "AbstractSupplier", + "org.apache.commons.io.channels.FileChannels" : "FileChannels", + "org.apache.commons.io.charset.CharsetDecoders" : "CharsetDecoders", + "org.apache.commons.io.charset.CharsetEncoders" : "CharsetEncoders", + "org.apache.commons.io.comparator.AbstractFileComparator" : "AbstractFileComparator", + "org.apache.commons.io.comparator.CompositeFileComparator" : "CompositeFileComparator", + "org.apache.commons.io.comparator.DefaultFileComparator" : "DefaultFileComparator", + "org.apache.commons.io.comparator.DirectoryFileComparator" : "DirectoryFileComparator", + "org.apache.commons.io.comparator.ExtensionFileComparator" : "ExtensionFileComparator", + "org.apache.commons.io.comparator.LastModifiedFileComparator" : "LastModifiedFileComparator", + "org.apache.commons.io.comparator.NameFileComparator" : "NameFileComparator", + "org.apache.commons.io.comparator.PathFileComparator" : "PathFileComparator", + "org.apache.commons.io.comparator.ReverseFileComparator" : "ReverseFileComparator", + "org.apache.commons.io.comparator.SizeFileComparator" : "SizeFileComparator", + "org.apache.commons.io.file.AccumulatorPathVisitor" : "AccumulatorPathVisitor", + "org.apache.commons.io.file.CleaningPathVisitor" : "CleaningPathVisitor", + "org.apache.commons.io.file.CopyDirectoryVisitor" : "CopyDirectoryVisitor", + "org.apache.commons.io.file.Counters" : "Counters", + "org.apache.commons.io.file.Counters$AbstractPathCounters" : "Counters.AbstractPathCounters", + "org.apache.commons.io.file.Counters$BigIntegerCounter" : "Counters.BigIntegerCounter", + "org.apache.commons.io.file.Counters$BigIntegerPathCounters" : "Counters.BigIntegerPathCounters", + "org.apache.commons.io.file.Counters$Counter" : "Counters.Counter", + "org.apache.commons.io.file.Counters$LongCounter" : "Counters.LongCounter", + "org.apache.commons.io.file.Counters$LongPathCounters" : "Counters.LongPathCounters", + "org.apache.commons.io.file.Counters$NoopCounter" : "Counters.NoopCounter", + "org.apache.commons.io.file.Counters$NoopPathCounters" : "Counters.NoopPathCounters", + "org.apache.commons.io.file.Counters$PathCounters" : "Counters.PathCounters", + "org.apache.commons.io.file.CountingPathVisitor" : "CountingPathVisitor", + "org.apache.commons.io.file.DeleteOption" : "DeleteOption", + "org.apache.commons.io.file.DeletingPathVisitor" : "DeletingPathVisitor", + "org.apache.commons.io.file.DirectoryStreamFilter" : "DirectoryStreamFilter", + "org.apache.commons.io.file.FilesUncheck" : "FilesUncheck", + "org.apache.commons.io.file.NoopPathVisitor" : "NoopPathVisitor", + "org.apache.commons.io.file.PathFilter" : "PathFilter", + "org.apache.commons.io.file.PathUtils" : "PathUtils", + "org.apache.commons.io.file.PathUtils$RelativeSortedPaths" : "PathUtils.RelativeSortedPaths", + "org.apache.commons.io.file.PathVisitor" : "PathVisitor", + "org.apache.commons.io.file.SimplePathVisitor" : "SimplePathVisitor", + "org.apache.commons.io.file.StandardDeleteOption" : "StandardDeleteOption", + "org.apache.commons.io.file.attribute.FileTimes" : "FileTimes", + "org.apache.commons.io.file.spi.FileSystemProviders" : "FileSystemProviders", + "org.apache.commons.io.filefilter.AbstractFileFilter" : "AbstractFileFilter", + "org.apache.commons.io.filefilter.AgeFileFilter" : "AgeFileFilter", + "org.apache.commons.io.filefilter.AndFileFilter" : "AndFileFilter", + "org.apache.commons.io.filefilter.CanExecuteFileFilter" : "CanExecuteFileFilter", + "org.apache.commons.io.filefilter.CanReadFileFilter" : "CanReadFileFilter", + "org.apache.commons.io.filefilter.CanWriteFileFilter" : "CanWriteFileFilter", + "org.apache.commons.io.filefilter.ConditionalFileFilter" : "ConditionalFileFilter", + "org.apache.commons.io.filefilter.DelegateFileFilter" : "DelegateFileFilter", + "org.apache.commons.io.filefilter.DirectoryFileFilter" : "DirectoryFileFilter", + "org.apache.commons.io.filefilter.EmptyFileFilter" : "EmptyFileFilter", + "org.apache.commons.io.filefilter.FalseFileFilter" : "FalseFileFilter", + "org.apache.commons.io.filefilter.FileEqualsFileFilter" : "FileEqualsFileFilter", + "org.apache.commons.io.filefilter.FileFileFilter" : "FileFileFilter", + "org.apache.commons.io.filefilter.FileFilterUtils" : "FileFilterUtils", + "org.apache.commons.io.filefilter.HiddenFileFilter" : "HiddenFileFilter", + "org.apache.commons.io.filefilter.IOFileFilter" : "IOFileFilter", + "org.apache.commons.io.filefilter.MagicNumberFileFilter" : "MagicNumberFileFilter", + "org.apache.commons.io.filefilter.NameFileFilter" : "NameFileFilter", + "org.apache.commons.io.filefilter.NotFileFilter" : "NotFileFilter", + "org.apache.commons.io.filefilter.OrFileFilter" : "OrFileFilter", + "org.apache.commons.io.filefilter.PathEqualsFileFilter" : "PathEqualsFileFilter", + "org.apache.commons.io.filefilter.PathMatcherFileFilter" : "PathMatcherFileFilter", + "org.apache.commons.io.filefilter.PathVisitorFileFilter" : "PathVisitorFileFilter", + "org.apache.commons.io.filefilter.PrefixFileFilter" : "PrefixFileFilter", + "org.apache.commons.io.filefilter.RegexFileFilter" : "RegexFileFilter", + "org.apache.commons.io.filefilter.SizeFileFilter" : "SizeFileFilter", + "org.apache.commons.io.filefilter.SuffixFileFilter" : "SuffixFileFilter", + "org.apache.commons.io.filefilter.SymbolicLinkFileFilter" : "SymbolicLinkFileFilter", + "org.apache.commons.io.filefilter.TrueFileFilter" : "TrueFileFilter", + "org.apache.commons.io.filefilter.WildcardFileFilter" : "WildcardFileFilter", + "org.apache.commons.io.filefilter.WildcardFileFilter$Builder" : "WildcardFileFilter.Builder", + "org.apache.commons.io.filefilter.WildcardFilter" : "WildcardFilter", + "org.apache.commons.io.function.Constants" : "Constants", + "org.apache.commons.io.function.Erase" : "Erase", + "org.apache.commons.io.function.IOBaseStream" : "IOBaseStream", + "org.apache.commons.io.function.IOBaseStreamAdapter" : "IOBaseStreamAdapter", + "org.apache.commons.io.function.IOBiConsumer" : "IOBiConsumer", + "org.apache.commons.io.function.IOBiFunction" : "IOBiFunction", + "org.apache.commons.io.function.IOBinaryOperator" : "IOBinaryOperator", + "org.apache.commons.io.function.IOComparator" : "IOComparator", + "org.apache.commons.io.function.IOConsumer" : "IOConsumer", + "org.apache.commons.io.function.IOFunction" : "IOFunction", + "org.apache.commons.io.function.IOIntSupplier" : "IOIntSupplier", + "org.apache.commons.io.function.IOIterator" : "IOIterator", + "org.apache.commons.io.function.IOIteratorAdapter" : "IOIteratorAdapter", + "org.apache.commons.io.function.IOLongSupplier" : "IOLongSupplier", + "org.apache.commons.io.function.IOPredicate" : "IOPredicate", + "org.apache.commons.io.function.IOQuadFunction" : "IOQuadFunction", + "org.apache.commons.io.function.IORunnable" : "IORunnable", + "org.apache.commons.io.function.IOSpliterator" : "IOSpliterator", + "org.apache.commons.io.function.IOSpliteratorAdapter" : "IOSpliteratorAdapter", + "org.apache.commons.io.function.IOStream" : "IOStream", + "org.apache.commons.io.function.IOStreamAdapter" : "IOStreamAdapter", + "org.apache.commons.io.function.IOStreams" : "IOStreams", + "org.apache.commons.io.function.IOSupplier" : "IOSupplier", + "org.apache.commons.io.function.IOTriConsumer" : "IOTriConsumer", + "org.apache.commons.io.function.IOTriFunction" : "IOTriFunction", + "org.apache.commons.io.function.IOUnaryOperator" : "IOUnaryOperator", + "org.apache.commons.io.function.Uncheck" : "Uncheck", + "org.apache.commons.io.function.UncheckedIOBaseStream" : "UncheckedIOBaseStream", + "org.apache.commons.io.function.UncheckedIOIterator" : "UncheckedIOIterator", + "org.apache.commons.io.function.UncheckedIOSpliterator" : "UncheckedIOSpliterator", + "org.apache.commons.io.input.AbstractCharacterFilterReader" : "AbstractCharacterFilterReader", + "org.apache.commons.io.input.AbstractInputStream" : "AbstractInputStream", + "org.apache.commons.io.input.AutoCloseInputStream" : "AutoCloseInputStream", + "org.apache.commons.io.input.AutoCloseInputStream$Builder" : "AutoCloseInputStream.Builder", + "org.apache.commons.io.input.BOMInputStream" : "BOMInputStream", + "org.apache.commons.io.input.BOMInputStream$Builder" : "BOMInputStream.Builder", + "org.apache.commons.io.input.BoundedInputStream" : "BoundedInputStream", + "org.apache.commons.io.input.BoundedInputStream$AbstractBuilder" : "BoundedInputStream.AbstractBuilder", + "org.apache.commons.io.input.BoundedInputStream$Builder" : "BoundedInputStream.Builder", + "org.apache.commons.io.input.BoundedReader" : "BoundedReader", + "org.apache.commons.io.input.BrokenInputStream" : "BrokenInputStream", + "org.apache.commons.io.input.BrokenReader" : "BrokenReader", + "org.apache.commons.io.input.BufferedFileChannelInputStream" : "BufferedFileChannelInputStream", + "org.apache.commons.io.input.BufferedFileChannelInputStream$Builder" : "BufferedFileChannelInputStream.Builder", + "org.apache.commons.io.input.ByteBufferCleaner" : "ByteBufferCleaner", + "org.apache.commons.io.input.ByteBufferCleaner$Cleaner" : "ByteBufferCleaner.Cleaner", + "org.apache.commons.io.input.ByteBufferCleaner$Java8Cleaner" : "ByteBufferCleaner.Java8Cleaner", + "org.apache.commons.io.input.ByteBufferCleaner$Java9Cleaner" : "ByteBufferCleaner.Java9Cleaner", + "org.apache.commons.io.input.CharSequenceInputStream" : "CharSequenceInputStream", + "org.apache.commons.io.input.CharSequenceInputStream$Builder" : "CharSequenceInputStream.Builder", + "org.apache.commons.io.input.CharSequenceReader" : "CharSequenceReader", + "org.apache.commons.io.input.CharacterFilterReader" : "CharacterFilterReader", + "org.apache.commons.io.input.CharacterSetFilterReader" : "CharacterSetFilterReader", + "org.apache.commons.io.input.ChecksumInputStream" : "ChecksumInputStream", + "org.apache.commons.io.input.ChecksumInputStream$Builder" : "ChecksumInputStream.Builder", + "org.apache.commons.io.input.CircularInputStream" : "CircularInputStream", + "org.apache.commons.io.input.ClassLoaderObjectInputStream" : "ClassLoaderObjectInputStream", + "org.apache.commons.io.input.CloseShieldInputStream" : "CloseShieldInputStream", + "org.apache.commons.io.input.CloseShieldReader" : "CloseShieldReader", + "org.apache.commons.io.input.ClosedInputStream" : "ClosedInputStream", + "org.apache.commons.io.input.ClosedReader" : "ClosedReader", + "org.apache.commons.io.input.CountingInputStream" : "CountingInputStream", + "org.apache.commons.io.input.DemuxInputStream" : "DemuxInputStream", + "org.apache.commons.io.input.InfiniteCircularInputStream" : "InfiniteCircularInputStream", + "org.apache.commons.io.input.Input" : "Input", + "org.apache.commons.io.input.MarkShieldInputStream" : "MarkShieldInputStream", + "org.apache.commons.io.input.MemoryMappedFileInputStream" : "MemoryMappedFileInputStream", + "org.apache.commons.io.input.MemoryMappedFileInputStream$Builder" : "MemoryMappedFileInputStream.Builder", + "org.apache.commons.io.input.MessageDigestCalculatingInputStream" : "MessageDigestCalculatingInputStream", + "org.apache.commons.io.input.MessageDigestCalculatingInputStream$Builder" : "MessageDigestCalculatingInputStream.Builder", + "org.apache.commons.io.input.MessageDigestCalculatingInputStream$MessageDigestMaintainingObserver" : "MessageDigestCalculatingInputStream.MessageDigestMaintainingObserver", + "org.apache.commons.io.input.MessageDigestInputStream" : "MessageDigestInputStream", + "org.apache.commons.io.input.MessageDigestInputStream$Builder" : "MessageDigestInputStream.Builder", + "org.apache.commons.io.input.MessageDigestInputStream$MessageDigestMaintainingObserver" : "MessageDigestInputStream.MessageDigestMaintainingObserver", + "org.apache.commons.io.input.NullInputStream" : "NullInputStream", + "org.apache.commons.io.input.NullReader" : "NullReader", + "org.apache.commons.io.input.ObservableInputStream" : "ObservableInputStream", + "org.apache.commons.io.input.ObservableInputStream$Observer" : "ObservableInputStream.Observer", + "org.apache.commons.io.input.ProxyInputStream" : "ProxyInputStream", + "org.apache.commons.io.input.ProxyReader" : "ProxyReader", + "org.apache.commons.io.input.QueueInputStream" : "QueueInputStream", + "org.apache.commons.io.input.QueueInputStream$Builder" : "QueueInputStream.Builder", + "org.apache.commons.io.input.RandomAccessFileInputStream" : "RandomAccessFileInputStream", + "org.apache.commons.io.input.RandomAccessFileInputStream$Builder" : "RandomAccessFileInputStream.Builder", + "org.apache.commons.io.input.ReadAheadInputStream" : "ReadAheadInputStream", + "org.apache.commons.io.input.ReadAheadInputStream$Builder" : "ReadAheadInputStream.Builder", + "org.apache.commons.io.input.ReaderInputStream" : "ReaderInputStream", + "org.apache.commons.io.input.ReaderInputStream$Builder" : "ReaderInputStream.Builder", + "org.apache.commons.io.input.ReversedLinesFileReader" : "ReversedLinesFileReader", + "org.apache.commons.io.input.ReversedLinesFileReader$Builder" : "ReversedLinesFileReader.Builder", + "org.apache.commons.io.input.ReversedLinesFileReader$FilePart" : "ReversedLinesFileReader.FilePart", + "org.apache.commons.io.input.SequenceReader" : "SequenceReader", + "org.apache.commons.io.input.SwappedDataInputStream" : "SwappedDataInputStream", + "org.apache.commons.io.input.TaggedInputStream" : "TaggedInputStream", + "org.apache.commons.io.input.TaggedReader" : "TaggedReader", + "org.apache.commons.io.input.Tailer" : "Tailer", + "org.apache.commons.io.input.Tailer$Builder" : "Tailer.Builder", + "org.apache.commons.io.input.Tailer$RandomAccessFileBridge" : "Tailer.RandomAccessFileBridge", + "org.apache.commons.io.input.Tailer$RandomAccessResourceBridge" : "Tailer.RandomAccessResourceBridge", + "org.apache.commons.io.input.Tailer$Tailable" : "Tailer.Tailable", + "org.apache.commons.io.input.Tailer$TailablePath" : "Tailer.TailablePath", + "org.apache.commons.io.input.TailerListener" : "TailerListener", + "org.apache.commons.io.input.TailerListenerAdapter" : "TailerListenerAdapter", + "org.apache.commons.io.input.TeeInputStream" : "TeeInputStream", + "org.apache.commons.io.input.TeeReader" : "TeeReader", + "org.apache.commons.io.input.ThrottledInputStream" : "ThrottledInputStream", + "org.apache.commons.io.input.ThrottledInputStream$Builder" : "ThrottledInputStream.Builder", + "org.apache.commons.io.input.TimestampedObserver" : "TimestampedObserver", + "org.apache.commons.io.input.UncheckedBufferedReader" : "UncheckedBufferedReader", + "org.apache.commons.io.input.UncheckedBufferedReader$Builder" : "UncheckedBufferedReader.Builder", + "org.apache.commons.io.input.UncheckedFilterInputStream" : "UncheckedFilterInputStream", + "org.apache.commons.io.input.UncheckedFilterInputStream$Builder" : "UncheckedFilterInputStream.Builder", + "org.apache.commons.io.input.UncheckedFilterReader" : "UncheckedFilterReader", + "org.apache.commons.io.input.UncheckedFilterReader$Builder" : "UncheckedFilterReader.Builder", + "org.apache.commons.io.input.UnixLineEndingInputStream" : "UnixLineEndingInputStream", + "org.apache.commons.io.input.UnsupportedOperationExceptions" : "UnsupportedOperationExceptions", + "org.apache.commons.io.input.UnsynchronizedBufferedInputStream" : "UnsynchronizedBufferedInputStream", + "org.apache.commons.io.input.UnsynchronizedBufferedInputStream$Builder" : "UnsynchronizedBufferedInputStream.Builder", + "org.apache.commons.io.input.UnsynchronizedBufferedReader" : "UnsynchronizedBufferedReader", + "org.apache.commons.io.input.UnsynchronizedByteArrayInputStream" : "UnsynchronizedByteArrayInputStream", + "org.apache.commons.io.input.UnsynchronizedByteArrayInputStream$Builder" : "UnsynchronizedByteArrayInputStream.Builder", + "org.apache.commons.io.input.UnsynchronizedFilterInputStream" : "UnsynchronizedFilterInputStream", + "org.apache.commons.io.input.UnsynchronizedFilterInputStream$Builder" : "UnsynchronizedFilterInputStream.Builder", + "org.apache.commons.io.input.UnsynchronizedReader" : "UnsynchronizedReader", + "org.apache.commons.io.input.WindowsLineEndingInputStream" : "WindowsLineEndingInputStream", + "org.apache.commons.io.input.XmlStreamReader" : "XmlStreamReader", + "org.apache.commons.io.input.XmlStreamReader$Builder" : "XmlStreamReader.Builder", + "org.apache.commons.io.input.XmlStreamReaderException" : "XmlStreamReaderException", + "org.apache.commons.io.input.buffer.CircularBufferInputStream" : "CircularBufferInputStream", + "org.apache.commons.io.input.buffer.CircularByteBuffer" : "CircularByteBuffer", + "org.apache.commons.io.input.buffer.PeekableInputStream" : "PeekableInputStream", + "org.apache.commons.io.monitor.FileAlterationListener" : "FileAlterationListener", + "org.apache.commons.io.monitor.FileAlterationListenerAdaptor" : "FileAlterationListenerAdaptor", + "org.apache.commons.io.monitor.FileAlterationMonitor" : "FileAlterationMonitor", + "org.apache.commons.io.monitor.FileAlterationObserver" : "FileAlterationObserver", + "org.apache.commons.io.monitor.FileEntry" : "FileEntry", + "org.apache.commons.io.monitor.SerializableFileTime" : "SerializableFileTime", + "org.apache.commons.io.output.AbstractByteArrayOutputStream" : "AbstractByteArrayOutputStream", + "org.apache.commons.io.output.AbstractByteArrayOutputStream$InputStreamConstructor" : "AbstractByteArrayOutputStream.InputStreamConstructor", + "org.apache.commons.io.output.AppendableOutputStream" : "AppendableOutputStream", + "org.apache.commons.io.output.AppendableWriter" : "AppendableWriter", + "org.apache.commons.io.output.BrokenOutputStream" : "BrokenOutputStream", + "org.apache.commons.io.output.BrokenWriter" : "BrokenWriter", + "org.apache.commons.io.output.ByteArrayOutputStream" : "ByteArrayOutputStream", + "org.apache.commons.io.output.ChunkedOutputStream" : "ChunkedOutputStream", + "org.apache.commons.io.output.ChunkedOutputStream$Builder" : "ChunkedOutputStream.Builder", + "org.apache.commons.io.output.ChunkedWriter" : "ChunkedWriter", + "org.apache.commons.io.output.CloseShieldOutputStream" : "CloseShieldOutputStream", + "org.apache.commons.io.output.CloseShieldWriter" : "CloseShieldWriter", + "org.apache.commons.io.output.ClosedOutputStream" : "ClosedOutputStream", + "org.apache.commons.io.output.ClosedWriter" : "ClosedWriter", + "org.apache.commons.io.output.CountingOutputStream" : "CountingOutputStream", + "org.apache.commons.io.output.DeferredFileOutputStream" : "DeferredFileOutputStream", + "org.apache.commons.io.output.DeferredFileOutputStream$Builder" : "DeferredFileOutputStream.Builder", + "org.apache.commons.io.output.DemuxOutputStream" : "DemuxOutputStream", + "org.apache.commons.io.output.FileWriterWithEncoding" : "FileWriterWithEncoding", + "org.apache.commons.io.output.FileWriterWithEncoding$Builder" : "FileWriterWithEncoding.Builder", + "org.apache.commons.io.output.FilterCollectionWriter" : "FilterCollectionWriter", + "org.apache.commons.io.output.LockableFileWriter" : "LockableFileWriter", + "org.apache.commons.io.output.LockableFileWriter$Builder" : "LockableFileWriter.Builder", + "org.apache.commons.io.output.NullAppendable" : "NullAppendable", + "org.apache.commons.io.output.NullOutputStream" : "NullOutputStream", + "org.apache.commons.io.output.NullPrintStream" : "NullPrintStream", + "org.apache.commons.io.output.NullWriter" : "NullWriter", + "org.apache.commons.io.output.ProxyCollectionWriter" : "ProxyCollectionWriter", + "org.apache.commons.io.output.ProxyOutputStream" : "ProxyOutputStream", + "org.apache.commons.io.output.ProxyWriter" : "ProxyWriter", + "org.apache.commons.io.output.QueueOutputStream" : "QueueOutputStream", + "org.apache.commons.io.output.StringBuilderWriter" : "StringBuilderWriter", + "org.apache.commons.io.output.TaggedOutputStream" : "TaggedOutputStream", + "org.apache.commons.io.output.TaggedWriter" : "TaggedWriter", + "org.apache.commons.io.output.TeeOutputStream" : "TeeOutputStream", + "org.apache.commons.io.output.TeeWriter" : "TeeWriter", + "org.apache.commons.io.output.ThresholdingOutputStream" : "ThresholdingOutputStream", + "org.apache.commons.io.output.UncheckedAppendable" : "UncheckedAppendable", + "org.apache.commons.io.output.UncheckedAppendableImpl" : "UncheckedAppendableImpl", + "org.apache.commons.io.output.UncheckedFilterOutputStream" : "UncheckedFilterOutputStream", + "org.apache.commons.io.output.UncheckedFilterOutputStream$Builder" : "UncheckedFilterOutputStream.Builder", + "org.apache.commons.io.output.UncheckedFilterWriter" : "UncheckedFilterWriter", + "org.apache.commons.io.output.UncheckedFilterWriter$Builder" : "UncheckedFilterWriter.Builder", + "org.apache.commons.io.output.UnsynchronizedByteArrayOutputStream" : "UnsynchronizedByteArrayOutputStream", + "org.apache.commons.io.output.UnsynchronizedByteArrayOutputStream$Builder" : "UnsynchronizedByteArrayOutputStream.Builder", + "org.apache.commons.io.output.WriterOutputStream" : "WriterOutputStream", + "org.apache.commons.io.output.WriterOutputStream$Builder" : "WriterOutputStream.Builder", + "org.apache.commons.io.output.XmlStreamWriter" : "XmlStreamWriter", + "org.apache.commons.io.output.XmlStreamWriter$Builder" : "XmlStreamWriter.Builder", + "org.apache.commons.io.serialization.ClassNameMatcher" : "ClassNameMatcher", + "org.apache.commons.io.serialization.FullClassNameMatcher" : "FullClassNameMatcher", + "org.apache.commons.io.serialization.RegexpClassNameMatcher" : "RegexpClassNameMatcher", + "org.apache.commons.io.serialization.ValidatingObjectInputStream" : "ValidatingObjectInputStream", + "org.apache.commons.io.serialization.WildcardClassNameMatcher" : "WildcardClassNameMatcher" + }, + "classpath" : "\/Users\/ktoso\/code\/swift-java\/Samples\/JavaDependencySampleApp\/.build\/swift-java-dependencies-C358EF3D-93BF-4D44-9523-80865DF0B59D\/build\/classes\/java\/main:\/Users\/ktoso\/code\/swift-java\/Samples\/JavaDependencySampleApp\/.build\/swift-java-dependencies-C358EF3D-93BF-4D44-9523-80865DF0B59D\/build\/resources\/main:\/Users\/ktoso\/.gradle\/caches\/modules-2\/files-2.1\/org.apache.commons\/commons-csv\/1.12.0\/c77e053d7189bc0857f8d323ab61cb949965fbd1\/commons-csv-1.12.0.jar:\/Users\/ktoso\/.gradle\/caches\/modules-2\/files-2.1\/commons-io\/commons-io\/2.17.0\/ddcc8433eb019fb48fe25207c0278143f3e1d7e2\/commons-io-2.17.0.jar:\/Users\/ktoso\/.gradle\/caches\/modules-2\/files-2.1\/commons-codec\/commons-codec\/1.17.1\/973638b7149d333563584137ebf13a691bb60579\/commons-codec-1.17.1.jar:\/Users\/ktoso\/code\/swift-java\/Samples\/JavaDependencySampleApp\/.build\/swift-java-dependencies-C358EF3D-93BF-4D44-9523-80865DF0B59D\/build\/classes\/java\/main:\/Users\/ktoso\/code\/swift-java\/Samples\/JavaDependencySampleApp\/.build\/swift-java-dependencies-C358EF3D-93BF-4D44-9523-80865DF0B59D\/build\/resources\/main:\/Users\/ktoso\/.gradle\/caches\/modules-2\/files-2.1\/org.apache.commons\/commons-csv\/1.12.0\/c77e053d7189bc0857f8d323ab61cb949965fbd1\/commons-csv-1.12.0.jar:\/Users\/ktoso\/.gradle\/caches\/modules-2\/files-2.1\/commons-io\/commons-io\/2.17.0\/ddcc8433eb019fb48fe25207c0278143f3e1d7e2\/commons-io-2.17.0.jar:\/Users\/ktoso\/.gradle\/caches\/modules-2\/files-2.1\/commons-codec\/commons-codec\/1.17.1\/973638b7149d333563584137ebf13a691bb60579\/commons-codec-1.17.1.jar:test.jar:\/Users\/ktoso\/code\/swift-java\/Samples\/JavaDependencySampleApp\/.build\/swift-java-dependencies-C358EF3D-93BF-4D44-9523-80865DF0B59D\/build\/classes\/java\/main:\/Users\/ktoso\/code\/swift-java\/Samples\/JavaDependencySampleApp\/.build\/swift-java-dependencies-C358EF3D-93BF-4D44-9523-80865DF0B59D\/build\/resources\/main:\/Users\/ktoso\/.gradle\/caches\/modules-2\/files-2.1\/org.apache.commons\/commons-csv\/1.12.0\/c77e053d7189bc0857f8d323ab61cb949965fbd1\/commons-csv-1.12.0.jar:\/Users\/ktoso\/.gradle\/caches\/modules-2\/files-2.1\/commons-io\/commons-io\/2.17.0\/ddcc8433eb019fb48fe25207c0278143f3e1d7e2\/commons-io-2.17.0.jar:\/Users\/ktoso\/.gradle\/caches\/modules-2\/files-2.1\/commons-codec\/commons-codec\/1.17.1\/973638b7149d333563584137ebf13a691bb60579\/commons-codec-1.17.1.jar:\/Users\/ktoso\/code\/swift-java\/Samples\/JavaDependencySampleApp\/.build\/swift-java-dependencies-C358EF3D-93BF-4D44-9523-80865DF0B59D\/build\/classes\/java\/main:\/Users\/ktoso\/code\/swift-java\/Samples\/JavaDependencySampleApp\/.build\/swift-java-dependencies-C358EF3D-93BF-4D44-9523-80865DF0B59D\/build\/resources\/main:\/Users\/ktoso\/.gradle\/caches\/modules-2\/files-2.1\/org.apache.commons\/commons-csv\/1.12.0\/c77e053d7189bc0857f8d323ab61cb949965fbd1\/commons-csv-1.12.0.jar:\/Users\/ktoso\/.gradle\/caches\/modules-2\/files-2.1\/commons-io\/commons-io\/2.17.0\/ddcc8433eb019fb48fe25207c0278143f3e1d7e2\/commons-io-2.17.0.jar:\/Users\/ktoso\/.gradle\/caches\/modules-2\/files-2.1\/commons-codec\/commons-codec\/1.17.1\/973638b7149d333563584137ebf13a691bb60579\/commons-codec-1.17.1.jar:test.jar:\/Users\/ktoso\/code\/swift-java\/Samples\/JavaDependencySampleApp\/.build\/swift-java-dependencies-C358EF3D-93BF-4D44-9523-80865DF0B59D\/build\/classes\/java\/main:\/Users\/ktoso\/code\/swift-java\/Samples\/JavaDependencySampleApp\/.build\/swift-java-dependencies-C358EF3D-93BF-4D44-9523-80865DF0B59D\/build\/resources\/main:\/Users\/ktoso\/.gradle\/caches\/modules-2\/files-2.1\/org.apache.commons\/commons-csv\/1.12.0\/c77e053d7189bc0857f8d323ab61cb949965fbd1\/commons-csv-1.12.0.jar:\/Users\/ktoso\/.gradle\/caches\/modules-2\/files-2.1\/commons-io\/commons-io\/2.17.0\/ddcc8433eb019fb48fe25207c0278143f3e1d7e2\/commons-io-2.17.0.jar:\/Users\/ktoso\/.gradle\/caches\/modules-2\/files-2.1\/commons-codec\/commons-codec\/1.17.1\/973638b7149d333563584137ebf13a691bb60579\/commons-codec-1.17.1.jar:\/Users\/ktoso\/code\/swift-java\/Samples\/JavaDependencySampleApp\/.build\/swift-java-dependencies-C358EF3D-93BF-4D44-9523-80865DF0B59D\/build\/classes\/java\/main:\/Users\/ktoso\/code\/swift-java\/Samples\/JavaDependencySampleApp\/.build\/swift-java-dependencies-C358EF3D-93BF-4D44-9523-80865DF0B59D\/build\/resources\/main:\/Users\/ktoso\/.gradle\/caches\/modules-2\/files-2.1\/org.apache.commons\/commons-csv\/1.12.0\/c77e053d7189bc0857f8d323ab61cb949965fbd1\/commons-csv-1.12.0.jar:\/Users\/ktoso\/.gradle\/caches\/modules-2\/files-2.1\/commons-io\/commons-io\/2.17.0\/ddcc8433eb019fb48fe25207c0278143f3e1d7e2\/commons-io-2.17.0.jar:\/Users\/ktoso\/.gradle\/caches\/modules-2\/files-2.1\/commons-codec\/commons-codec\/1.17.1\/973638b7149d333563584137ebf13a691bb60579\/commons-codec-1.17.1.jar:test.jar:\/Users\/ktoso\/code\/swift-java\/Samples\/JavaDependencySampleApp\/.build\/swift-java-dependencies-C358EF3D-93BF-4D44-9523-80865DF0B59D\/build\/classes\/java\/main:\/Users\/ktoso\/code\/swift-java\/Samples\/JavaDependencySampleApp\/.build\/swift-java-dependencies-C358EF3D-93BF-4D44-9523-80865DF0B59D\/build\/resources\/main:\/Users\/ktoso\/.gradle\/caches\/modules-2\/files-2.1\/org.apache.commons\/commons-csv\/1.12.0\/c77e053d7189bc0857f8d323ab61cb949965fbd1\/commons-csv-1.12.0.jar:\/Users\/ktoso\/.gradle\/caches\/modules-2\/files-2.1\/commons-io\/commons-io\/2.17.0\/ddcc8433eb019fb48fe25207c0278143f3e1d7e2\/commons-io-2.17.0.jar:\/Users\/ktoso\/.gradle\/caches\/modules-2\/files-2.1\/commons-codec\/commons-codec\/1.17.1\/973638b7149d333563584137ebf13a691bb60579\/commons-codec-1.17.1.jar:\/Users\/ktoso\/code\/swift-java\/Samples\/JavaDependencySampleApp\/.build\/swift-java-dependencies-C358EF3D-93BF-4D44-9523-80865DF0B59D\/build\/classes\/java\/main:\/Users\/ktoso\/code\/swift-java\/Samples\/JavaDependencySampleApp\/.build\/swift-java-dependencies-C358EF3D-93BF-4D44-9523-80865DF0B59D\/build\/resources\/main:\/Users\/ktoso\/.gradle\/caches\/modules-2\/files-2.1\/org.apache.commons\/commons-csv\/1.12.0\/c77e053d7189bc0857f8d323ab61cb949965fbd1\/commons-csv-1.12.0.jar:\/Users\/ktoso\/.gradle\/caches\/modules-2\/files-2.1\/commons-io\/commons-io\/2.17.0\/ddcc8433eb019fb48fe25207c0278143f3e1d7e2\/commons-io-2.17.0.jar:\/Users\/ktoso\/.gradle\/caches\/modules-2\/files-2.1\/commons-codec\/commons-codec\/1.17.1\/973638b7149d333563584137ebf13a691bb60579\/commons-codec-1.17.1.jar:test.jar:\/Users\/ktoso\/code\/swift-java\/Samples\/JavaDependencySampleApp\/.build\/swift-java-dependencies-C358EF3D-93BF-4D44-9523-80865DF0B59D\/build\/classes\/java\/main:\/Users\/ktoso\/code\/swift-java\/Samples\/JavaDependencySampleApp\/.build\/swift-java-dependencies-C358EF3D-93BF-4D44-9523-80865DF0B59D\/build\/resources\/main:\/Users\/ktoso\/.gradle\/caches\/modules-2\/files-2.1\/org.apache.commons\/commons-csv\/1.12.0\/c77e053d7189bc0857f8d323ab61cb949965fbd1\/commons-csv-1.12.0.jar:\/Users\/ktoso\/.gradle\/caches\/modules-2\/files-2.1\/commons-io\/commons-io\/2.17.0\/ddcc8433eb019fb48fe25207c0278143f3e1d7e2\/commons-io-2.17.0.jar:\/Users\/ktoso\/.gradle\/caches\/modules-2\/files-2.1\/commons-codec\/commons-codec\/1.17.1\/973638b7149d333563584137ebf13a691bb60579\/commons-codec-1.17.1.jar:\/Users\/ktoso\/code\/swift-java\/Samples\/JavaDependencySampleApp\/.build\/swift-java-dependencies-C358EF3D-93BF-4D44-9523-80865DF0B59D\/build\/classes\/java\/main:\/Users\/ktoso\/code\/swift-java\/Samples\/JavaDependencySampleApp\/.build\/swift-java-dependencies-C358EF3D-93BF-4D44-9523-80865DF0B59D\/build\/resources\/main:\/Users\/ktoso\/.gradle\/caches\/modules-2\/files-2.1\/org.apache.commons\/commons-csv\/1.12.0\/c77e053d7189bc0857f8d323ab61cb949965fbd1\/commons-csv-1.12.0.jar:\/Users\/ktoso\/.gradle\/caches\/modules-2\/files-2.1\/commons-io\/commons-io\/2.17.0\/ddcc8433eb019fb48fe25207c0278143f3e1d7e2\/commons-io-2.17.0.jar:\/Users\/ktoso\/.gradle\/caches\/modules-2\/files-2.1\/commons-codec\/commons-codec\/1.17.1\/973638b7149d333563584137ebf13a691bb60579\/commons-codec-1.17.1.jar:test.jar:test.jar:test.jar:test.jar:test.jar:test.jar:configure:test.jar:configure" +} diff --git a/Samples/JavaSieve/Package.swift b/Samples/JavaSieve/Package.swift index cd65f82e..65c10481 100644 --- a/Samples/JavaSieve/Package.swift +++ b/Samples/JavaSieve/Package.swift @@ -54,6 +54,7 @@ let package = Package( .product(name: "JavaKit", package: "swift-java"), .product(name: "JavaKitJar", package: "swift-java"), ], + exclude: ["swift-java.config"], swiftSettings: [ .unsafeFlags(["-I\(javaIncludePath)", "-I\(javaPlatformIncludePath)"]) ], @@ -71,6 +72,7 @@ let package = Package( .product(name: "JavaKit", package: "swift-java"), .product(name: "JavaKitCollection", package: "swift-java"), ], + exclude: ["swift-java.config"], swiftSettings: [ .unsafeFlags(["-I\(javaIncludePath)", "-I\(javaPlatformIncludePath)"]) ], diff --git a/Samples/JavaSieve/Sources/JavaSieve/main.swift b/Samples/JavaSieve/Sources/JavaSieve/main.swift index e2047713..feec792d 100644 --- a/Samples/JavaSieve/Sources/JavaSieve/main.swift +++ b/Samples/JavaSieve/Sources/JavaSieve/main.swift @@ -15,10 +15,7 @@ import JavaKit import JavaMath -let jvm = try JavaVirtualMachine.shared(classpath: [ - "quadratic-sieve-Java/build/libs/QuadraticSieve-1.0.jar", - ".", -]) +let jvm = try JavaVirtualMachine.shared() do { let sieveClass = try JavaClass(environment: jvm.environment()) @@ -26,7 +23,7 @@ do { print("Found prime: \(prime.intValue())") } - try JavaClass().HALF_UP + _ = try JavaClass().HALF_UP // can import a Java enum value } catch { print("Failure: \(error)") } diff --git a/Samples/JavaSieve/Sources/JavaSieve/swift-java.config b/Samples/JavaSieve/Sources/JavaSieve/swift-java.config index 7e055d1c..40d01d4b 100644 --- a/Samples/JavaSieve/Sources/JavaSieve/swift-java.config +++ b/Samples/JavaSieve/Sources/JavaSieve/swift-java.config @@ -29,3 +29,4 @@ "com.gazman.quadratic_sieve.wheel.Wheel" : "Wheel" } } + diff --git a/Sources/JavaKitConfigurationShared/Configuration.swift b/Sources/JavaKitConfigurationShared/Configuration.swift index 2314a1b8..2298b9fa 100644 --- a/Sources/JavaKitConfigurationShared/Configuration.swift +++ b/Sources/JavaKitConfigurationShared/Configuration.swift @@ -137,13 +137,13 @@ public func readConfiguration(configPath: URL, file: String = #fileID, line: UIn } } -public func findSwiftJavaClasspaths(moduleName: String) -> [String] { +public func findSwiftJavaClasspaths(swiftModule: String) -> [String] { let basePath: String = FileManager.default.currentDirectoryPath let pluginOutputsDir = URL(fileURLWithPath: basePath) .appendingPathComponent(".build", isDirectory: true) .appendingPathComponent("plugins", isDirectory: true) .appendingPathComponent("outputs", isDirectory: true) - .appendingPathComponent(moduleName, isDirectory: true) + .appendingPathComponent(swiftModule, isDirectory: true) return findSwiftJavaClasspaths(in: pluginOutputsDir.path) } diff --git a/Sources/SwiftJavaTool/Commands/ConfigureCommand.swift b/Sources/SwiftJavaTool/Commands/ConfigureCommand.swift new file mode 100644 index 00000000..8574888d --- /dev/null +++ b/Sources/SwiftJavaTool/Commands/ConfigureCommand.swift @@ -0,0 +1,238 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2024 Apple Inc. and the Swift.org project authors +// Licensed under Apache License v2.0 +// +// See LICENSE.txt for license information +// See CONTRIBUTORS.txt for the list of Swift.org project authors +// +// SPDX-License-Identifier: Apache-2.0 +// +//===----------------------------------------------------------------------===// + +import ArgumentParser +import Foundation +import SwiftJavaLib +import JExtractSwiftLib +import JavaKit +import JavaKitJar +import JavaKitNetwork +import JavaKitReflection +import SwiftSyntax +import SwiftSyntaxBuilder +import JavaKitConfigurationShared +import JavaKitShared + +extension SwiftJava { + struct ConfigureCommand: SwiftJavaBaseAsyncParsableCommand, HasCommonOptions, HasCommonJVMOptions { + static let configuration = CommandConfiguration( + commandName: "configure", + abstract: "Configure and emit a swift-java.config file based on an input dependency or jar file") + + @OptionGroup var commonOptions: SwiftJava.CommonOptions + @OptionGroup var commonJVMOptions: SwiftJava.CommonJVMOptions + + // TODO: This should be a "make wrappers" option that just detects when we give it a jar + @Flag( + help: "Specifies that the input is a *.jar file whose public classes will be loaded. The output of swift-java will be a configuration file (swift-java.config) that can be used as input to a subsequent swift-java invocation to generate wrappers for those public classes." + ) + var jar: Bool = false + + @Option( + name: .long, + help: "How to handle an existing swift-java.config; by default 'overwrite' by can be changed to amending a configuration" + ) + var existingConfigFile: ExistingConfigFileMode = .overwrite + enum ExistingConfigFileMode: String, ExpressibleByArgument, Codable { + case overwrite + case amend + } + + @Option(help: "The name of the Swift module into which the resulting Swift types will be generated.") + var swiftModule: String + + var effectiveSwiftModule: String { + swiftModule + } + + @Argument( + help: "The input file, which is either a swift-java configuration file or (if '-jar' was specified) a Jar file." + ) + var input: String? + } +} + +extension SwiftJava.ConfigureCommand { + mutating func runSwiftJavaCommand(config: inout Configuration) async throws { + // Form a class path from all of our input sources: + // * Command-line option --classpath + let classpathOptionEntries: [String] = self.commonJVMOptions.classpath.flatMap { $0.split(separator: ":").map(String.init) } + let classpathFromEnv = ProcessInfo.processInfo.environment["CLASSPATH"]?.split(separator: ":").map(String.init) ?? [] + let classpathFromConfig: [String] = config.classpath?.split(separator: ":").map(String.init) ?? [] + print("[debug][swift-java] Base classpath from config: \(classpathFromConfig)") + + var classpathEntries: [String] = classpathFromConfig + + let swiftJavaCachedModuleClasspath = findSwiftJavaClasspaths(in: + // self.effectiveCacheDirectory ?? + FileManager.default.currentDirectoryPath) + print("[debug][swift-java] Classpath from *.swift-java.classpath files: \(swiftJavaCachedModuleClasspath)") + classpathEntries += swiftJavaCachedModuleClasspath + + if !classpathOptionEntries.isEmpty { + print("[debug][swift-java] Classpath from options: \(classpathOptionEntries)") + classpathEntries += classpathOptionEntries + } else { + // * Base classpath from CLASSPATH env variable + print("[debug][swift-java] Classpath from environment: \(classpathFromEnv)") + classpathEntries += classpathFromEnv + } + + let extraClasspath = input ?? "" // FIXME: just use the -cp as usual + let extraClasspathEntries = extraClasspath.split(separator: ":").map(String.init) + print("[debug][swift-java] Extra classpath: \(extraClasspathEntries)") + classpathEntries += extraClasspathEntries + + // Bring up the Java VM when necessary + + if logLevel >= .debug { + let classpathString = classpathEntries.joined(separator: ":") + print("[debug][swift-java] Initialize JVM with classpath: \(classpathString)") + } + let jvm = try JavaVirtualMachine.shared(classpath: classpathEntries) + + try emitConfiguration(classpath: self.commonJVMOptions.classpath, environment: jvm.environment()) + } + + /// Get base configuration, depending on if we are to 'amend' or 'overwrite' the existing configuration. + func getBaseConfigurationForWrite() throws -> (Bool, Configuration) { + guard let actualOutputDirectory = self.actualOutputDirectory else { + // If output has no path there's nothing to amend + return (false, .init()) + } + + switch self.existingConfigFile { + case .overwrite: + // always make up a fresh instance if we're overwriting + return (false, .init()) + case .amend: + let configPath = actualOutputDirectory + guard let config = try readConfiguration(sourceDir: configPath.path) else { + return (false, .init()) + } + return (true, config) + } + } + + // TODO: make this perhaps "emit type mappings" + mutating func emitConfiguration( + classpath: [String], + environment: JNIEnvironment + ) throws { + if let filterJavaPackage = self.commonJVMOptions.filterJavaPackage { + print("[java-swift][debug] Generate Java->Swift type mappings. Active filter: \(filterJavaPackage)") + } + print("[java-swift][debug] Classpath: \(classpath)") + + if classpath.isEmpty { + print("[java-swift][warning] Classpath is empty!") + } + + // Get a fresh or existing configuration we'll amend + var (amendExistingConfig, configuration) = try getBaseConfigurationForWrite() + if amendExistingConfig { + print("[swift-java] Amend existing swift-java.config file...") + } + configuration.classpath = classpath.joined(separator: ":") // TODO: is this correct? + + // Import types from all the classpath entries; + // Note that we use the package level filtering, so users have some control over what gets imported. + let classpathEntries = classpath.split(separator: ":").map(String.init) + for entry in classpathEntries { + guard fileOrDirectoryExists(at: entry) else { + // We only log specific jars missing, as paths may be empty directories that won't hurt not existing. + print("[debug][swift-java] Classpath entry does not exist: \(entry)") + continue + } + + print("[debug][swift-java] Importing classpath entry: \(entry)") + if entry.hasSuffix(".jar") { + let jarFile = try JarFile(entry, false, environment: environment) + try addJavaToSwiftMappings( + to: &configuration, + forJar: jarFile, + environment: environment + ) + } else if FileManager.default.fileExists(atPath: entry) { + print("[warning][swift-java] Currently unable handle directory classpath entries for config generation! Skipping: \(entry)") + } else { + print("[warning][swift-java] Classpath entry does not exist, skipping: \(entry)") + } + } + + // Encode the configuration. + let contents = try configuration.renderJSON() + + // Write the file. + try writeContents( + contents, + to: "swift-java.config", + description: "swift-java configuration file" + ) + } + + mutating func addJavaToSwiftMappings( + to configuration: inout Configuration, + forJar jarFile: JarFile, + environment: JNIEnvironment + ) throws { + for entry in jarFile.entries()! { + // We only look at class files in the Jar file. + guard entry.getName().hasSuffix(".class") else { + continue + } + + // Skip some "common" files we know that would be duplicated in every jar + guard !entry.getName().hasPrefix("META-INF") else { + continue + } + guard !entry.getName().hasSuffix("package-info") else { + continue + } + guard !entry.getName().hasSuffix("package-info.class") else { + continue + } + + // If this is a local class, it cannot be mapped into Swift. + if entry.getName().isLocalJavaClass { + continue + } + + let javaCanonicalName = String(entry.getName().replacing("/", with: ".") + .dropLast(".class".count)) + + if let filterJavaPackage = self.commonJVMOptions.filterJavaPackage, + !javaCanonicalName.hasPrefix(filterJavaPackage) { + // Skip classes which don't match our expected prefix + continue + } + + if configuration.classes?[javaCanonicalName] != nil { + // We never overwrite an existing class mapping configuration. + // E.g. the user may have configured a custom name for a type. + continue + } + + configuration.classes?[javaCanonicalName] = + javaCanonicalName.defaultSwiftNameForJavaClass + } + } + +} + +package func fileOrDirectoryExists(at path: String) -> Bool { + var isDirectory: ObjCBool = false + return FileManager.default.fileExists(atPath: path, isDirectory: &isDirectory) +} \ No newline at end of file diff --git a/Sources/SwiftJavaTool/SwiftJava+FetchDependencies.swift b/Sources/SwiftJavaTool/Commands/ResolveCommand.swift similarity index 86% rename from Sources/SwiftJavaTool/SwiftJava+FetchDependencies.swift rename to Sources/SwiftJavaTool/Commands/ResolveCommand.swift index 47570b19..eb96e490 100644 --- a/Sources/SwiftJavaTool/SwiftJava+FetchDependencies.swift +++ b/Sources/SwiftJavaTool/Commands/ResolveCommand.swift @@ -2,7 +2,7 @@ // // This source file is part of the Swift.org open source project // -// Copyright (c) 2024 Apple Inc. and the Swift.org project authors +// Copyright (c) 2024-2025 Apple Inc. and the Swift.org project authors // Licensed under Apache License v2.0 // // See LICENSE.txt for license information @@ -12,6 +12,7 @@ // //===----------------------------------------------------------------------===// +import ArgumentParser import Foundation import SwiftJavaLib import JavaKit @@ -23,12 +24,37 @@ import JavaKitShared import _Subprocess extension SwiftJava { + struct ResolveCommand: SwiftJavaBaseAsyncParsableCommand { + static let configuration = CommandConfiguration( + commandName: "resolve", + abstract: "Resolve dependencies and write the resulting swift-java.classpath file") + @OptionGroup var commonOptions: SwiftJava.CommonOptions + + @Option(help: "The name of the Swift module into which the resulting Swift types will be generated.") + var swiftModule: String + + var effectiveSwiftModule: String { + swiftModule + } + + } +} + +extension SwiftJava.ResolveCommand { + mutating func runSwiftJavaCommand(config: inout Configuration) async throws { + fatalError("NOT IMPLEMENTED: resolve") + } +} + + + +extension SwiftJava { var SwiftJavaClasspathPrefix: String { "SWIFT_JAVA_CLASSPATH:" } var printRuntimeClasspathTaskName: String { "printRuntimeClasspath" } - func fetchDependencies(moduleName: String, + func fetchDependencies(swiftModule: String, dependencies: [JavaDependencyDescriptor]) async throws -> ResolvedDependencyClasspath { let deps = dependencies.map { $0.descriptionGradleStyle } print("[debug][swift-java] Resolve and fetch dependencies for: \(deps)") @@ -37,7 +63,7 @@ extension SwiftJava { let classpathEntries = dependenciesClasspath.split(separator: ":") - print("[info][swift-java] Resolved classpath for \(deps.count) dependencies of '\(moduleName)', classpath entries: \(classpathEntries.count), ", terminator: "") + print("[info][swift-java] Resolved classpath for \(deps.count) dependencies of '\(swiftModule)', classpath entries: \(classpathEntries.count), ", terminator: "") print("done.".green) for entry in classpathEntries { @@ -128,7 +154,7 @@ extension SwiftJava { } mutating func writeFetchedDependenciesClasspath( - moduleName: String, + swiftModule: String, cacheDir: String, resolvedClasspath: ResolvedDependencyClasspath) throws { // Convert the artifact name to a module name @@ -137,14 +163,14 @@ extension SwiftJava { // The file contents are just plain let contents = resolvedClasspath.classpath - print("[debug][swift-java] Resolved dependency: \(classpath)") + print("[debug][swift-java] Resolved dependency: \(commonJVMOptions.classpath)") // Write the file try writeContents( contents, outputDirectoryOverride: URL(fileURLWithPath: cacheDir), - to: "\(moduleName).swift-java.classpath", - description: "swift-java.classpath file for module \(moduleName)" + to: "\(swiftModule).swift-java.classpath", + description: "swift-java.classpath file for module \(swiftModule)" ) } diff --git a/Sources/SwiftJavaTool/SwiftJava+GenerateWrappers.swift b/Sources/SwiftJavaTool/Commands/SwiftJava+GenerateWrappers.swift similarity index 95% rename from Sources/SwiftJavaTool/SwiftJava+GenerateWrappers.swift rename to Sources/SwiftJavaTool/Commands/SwiftJava+GenerateWrappers.swift index a57644de..676b278d 100644 --- a/Sources/SwiftJavaTool/SwiftJava+GenerateWrappers.swift +++ b/Sources/SwiftJavaTool/Commands/SwiftJava+GenerateWrappers.swift @@ -27,11 +27,8 @@ extension SwiftJava { dependentConfigs: [(String, Configuration)], environment: JNIEnvironment ) throws { - guard let moduleName else { - fatalError("--module-name must be set in 'generate wrappers' mode!") - } let translator = JavaTranslator( - swiftModuleName: moduleName, + swiftModuleName: effectiveSwiftModule, environment: environment, translateAsClass: true ) @@ -49,7 +46,7 @@ extension SwiftJava { } // Add the configuration for this module. - translator.addConfiguration(config, forSwiftModule: moduleName) + translator.addConfiguration(config, forSwiftModule: effectiveSwiftModule) // Load all of the explicitly-requested classes. let classLoader = try JavaClass(environment: environment) diff --git a/Sources/SwiftJavaTool/SwiftJava+JExtract.swift b/Sources/SwiftJavaTool/Commands/SwiftJava+JExtract.swift similarity index 100% rename from Sources/SwiftJavaTool/SwiftJava+JExtract.swift rename to Sources/SwiftJavaTool/Commands/SwiftJava+JExtract.swift diff --git a/Sources/SwiftJavaTool/CommonOptions.swift b/Sources/SwiftJavaTool/CommonOptions.swift new file mode 100644 index 00000000..43a35a5d --- /dev/null +++ b/Sources/SwiftJavaTool/CommonOptions.swift @@ -0,0 +1,59 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2024 Apple Inc. and the Swift.org project authors +// Licensed under Apache License v2.0 +// +// See LICENSE.txt for license information +// See CONTRIBUTORS.txt for the list of Swift.org project authors +// +// SPDX-License-Identifier: Apache-2.0 +// +//===----------------------------------------------------------------------===// + +import ArgumentParser +import Foundation +import SwiftJavaLib +import JExtractSwiftLib +import JavaKit +import JavaKitJar +import JavaKitNetwork +import JavaKitReflection +import SwiftSyntax +import SwiftSyntaxBuilder +import JavaKitConfigurationShared +import JavaKitShared + +protocol HasCommonOptions { + var commonOptions: SwiftJava.CommonOptions { get set } +} + +protocol HasCommonJVMOptions { + var commonJVMOptions: SwiftJava.CommonJVMOptions { get set } +} + +extension SwiftJava { + struct CommonOptions: ParsableArguments { + // TODO: clarify this vs outputSwift (history: outputSwift is jextract, and this was java2swift) + @Option(name: .shortAndLong, help: "The directory in which to output the generated Swift files or the SwiftJava configuration file.") + var outputDirectory: String? = nil + + @Option(help: "Directory containing Swift files which should be extracted into Java bindings. Also known as 'jextract' mode. Must be paired with --output-java and --output-swift.") + var inputSwift: String? = nil + + @Option(name: .shortAndLong, help: "Configure the level of logs that should be printed") + var logLevel: Logger.Level = .info + } + + struct CommonJVMOptions: ParsableArguments { + @Option( + name: [.customLong("cp"), .customLong("classpath")], + help: "Class search path of directories and zip/jar files from which Java classes can be loaded." + ) + var classpath: [String] = [] + + @Option(name: .shortAndLong, help: "While scanning a classpath, inspect only types included in this package") + var filterJavaPackage: String? = nil + } +} \ No newline at end of file diff --git a/Sources/SwiftJavaTool/Java/JavaClassLoader.swift b/Sources/SwiftJavaTool/Java/JavaClassLoader.swift new file mode 100644 index 00000000..41492d53 --- /dev/null +++ b/Sources/SwiftJavaTool/Java/JavaClassLoader.swift @@ -0,0 +1,29 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2024 Apple Inc. and the Swift.org project authors +// Licensed under Apache License v2.0 +// +// See LICENSE.txt for license information +// See CONTRIBUTORS.txt for the list of Swift.org project authors +// +// SPDX-License-Identifier: Apache-2.0 +// +//===----------------------------------------------------------------------===// + +import SwiftJavaLib +import JavaKitShared +import JavaRuntime +import JavaKit + +@JavaClass("java.lang.ClassLoader") +public struct ClassLoader { + @JavaMethod + public func loadClass(_ arg0: String) throws -> JavaClass? +} + +extension JavaClass { + @JavaStaticMethod + public func getSystemClassLoader() -> ClassLoader? +} \ No newline at end of file diff --git a/Sources/SwiftJavaTool/SwiftJava+EmitConfiguration.swift b/Sources/SwiftJavaTool/SwiftJava+EmitConfiguration.swift deleted file mode 100644 index e029d2db..00000000 --- a/Sources/SwiftJavaTool/SwiftJava+EmitConfiguration.swift +++ /dev/null @@ -1,120 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift.org open source project -// -// Copyright (c) 2024 Apple Inc. and the Swift.org project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of Swift.org project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -import Foundation -import ArgumentParser -import SwiftJavaLib -import JavaKit -import JavaKitJar -import JavaKitConfigurationShared - -extension SwiftJava { - - // TODO: make this perhaps "emit type mappings" - mutating func emitConfiguration( - classpath: String, - environment: JNIEnvironment - ) throws { - print("[java-swift] Generate Java->Swift type mappings. Active filter: \(javaPackageFilter)") - print("[java-swift] Classpath: \(classpath)") - - if classpath.isEmpty { - print("[warning][java-swift] Classpath is empty!") - } - - // Get a fresh or existing configuration we'll amend - var (amendExistingConfig, configuration) = try getBaseConfigurationForWrite() - if amendExistingConfig { - print("[swift-java] Amend existing swift-java.config file...") - } - configuration.classpath = classpath // TODO: is this correct? - - // Import types from all the classpath entries; - // Note that we use the package level filtering, so users have some control over what gets imported. - for entry in classpath.split(separator: ":").map(String.init) { - print("[debug][swift-java] Importing classpath entry: \(entry)") - if entry.hasSuffix(".jar") { - let jarFile = try JarFile(entry, false, environment: environment) - try addJavaToSwiftMappings( - to: &configuration, - forJar: jarFile, - environment: environment - ) - } else if FileManager.default.fileExists(atPath: entry) { - print("[warning][swift-java] Currently unable handle directory classpath entries for config generation! Skipping: \(entry)") - } else { - print("[warning][swift-java] Classpath entry does not exist, skipping: \(entry)") - } - } - - // Encode the configuration. - let contents = try configuration.renderJSON() - - // Write the file. - try writeContents( - contents, - to: "swift-java.config", - description: "swift-java configuration file" - ) - } - - mutating func addJavaToSwiftMappings( - to configuration: inout Configuration, - forJar jarFile: JarFile, - environment: JNIEnvironment - ) throws { - for entry in jarFile.entries()! { - // We only look at class files in the Jar file. - guard entry.getName().hasSuffix(".class") else { - continue - } - - // Skip some "common" files we know that would be duplicated in every jar - guard !entry.getName().hasPrefix("META-INF") else { - continue - } - guard !entry.getName().hasSuffix("package-info") else { - continue - } - guard !entry.getName().hasSuffix("package-info.class") else { - continue - } - - // If this is a local class, it cannot be mapped into Swift. - if entry.getName().isLocalJavaClass { - continue - } - - let javaCanonicalName = String(entry.getName().replacing("/", with: ".") - .dropLast(".class".count)) - - if let javaPackageFilter { - if !javaCanonicalName.hasPrefix(javaPackageFilter) { - // Skip classes which don't match our expected prefix - continue - } - } - - if configuration.classes?[javaCanonicalName] != nil { - // We never overwrite an existing class mapping configuration. - // E.g. the user may have configured a custom name for a type. - continue - } - - configuration.classes?[javaCanonicalName] = - javaCanonicalName.defaultSwiftNameForJavaClass - } - } - -} \ No newline at end of file diff --git a/Sources/SwiftJavaTool/SwiftJava.swift b/Sources/SwiftJavaTool/SwiftJava.swift index cae7a2a4..0e6e64ae 100644 --- a/Sources/SwiftJavaTool/SwiftJava.swift +++ b/Sources/SwiftJavaTool/SwiftJava.swift @@ -27,11 +27,22 @@ import JavaKitShared /// Command-line utility to drive the export of Java classes into Swift types. @main -struct SwiftJava: AsyncParsableCommand { +struct SwiftJava: SwiftJavaBaseAsyncParsableCommand { // FIXME: this is just a normal async command, no parsing happening here static var _commandName: String { "swift-java" } + static let configuration = CommandConfiguration( + abstract: "Generate sources and configuration for Swift and Java interoperability.", + subcommands: [ + ConfigureCommand.self, + ResolveCommand.self, + ]) + @Option(help: "The name of the Swift module into which the resulting Swift types will be generated.") - var moduleName: String? // TODO: rename to --swift-module? + var swiftModule: String? + + var effectiveSwiftModule: String { + swiftModule ?? "UnknownSwiftModule" + } @Option( help: @@ -39,30 +50,14 @@ struct SwiftJava: AsyncParsableCommand { ) var dependsOn: [String] = [] - // TODO: This should be a "make wrappers" option that just detects when we give it a jar - @Flag( - help: - "Specifies that the input is a Jar file whose public classes will be loaded. The output of Java2Swift will be a configuration file (Java2Swift.config) that can be used as input to a subsequent Java2Swift invocation to generate wrappers for those public classes." - ) - var jar: Bool = false - @Flag(help: "Fetch dependencies from given target (containing swift-java configuration) or dependency string") var fetch: Bool = false - @Option( - name: [.customLong("cp"), .customLong("classpath")], - help: "Class search path of directories and zip/jar files from which Java classes can be loaded." - ) - var classpath: [String] = [] - @Option( help: "The names of Java classes whose declared native methods will be implemented in Swift." ) var swiftNativeImplementation: [String] = [] - @Option(help: "Directory containing Swift files which should be extracted into Java bindings. Also known as 'jextract' mode. Must be paired with --output-java and --output-swift.") - var inputSwift: String? = nil - @Option(help: "The directory where generated Swift files should be written. Generally used with jextract mode.") var outputSwift: String? = nil @@ -75,107 +70,36 @@ struct SwiftJava: AsyncParsableCommand { @Option(help: "The mode of generation to use for the output files. Used with jextract mode.") var mode: GenerationMode = .ffm - // TODO: clarify this vs outputSwift (history: outputSwift is jextract, and this was java2swift) - @Option(name: .shortAndLong, help: "The directory in which to output the generated Swift files or the SwiftJava configuration file.") - var outputDirectory: String? = nil +// // TODO: clarify this vs outputSwift (history: outputSwift is jextract, and this was java2swift) +// @Option(name: .shortAndLong, help: "The directory in which to output the generated Swift files or the SwiftJava configuration file.") +// var outputDirectory: String? = nil @Option(name: .shortAndLong, help: "Directory where to write cached values (e.g. swift-java.classpath files)") var cacheDirectory: String? = nil - @Option(name: .shortAndLong, help: "Configure the level of logs that should be printed") - var logLevel: Logger.Level = .info + @OptionGroup var commonOptions: SwiftJava.CommonOptions + @OptionGroup var commonJVMOptions: SwiftJava.CommonJVMOptions var effectiveCacheDirectory: String? { if let cacheDirectory { return cacheDirectory - } else if let outputDirectory { + } else if let outputDirectory = commonOptions.outputDirectory { return outputDirectory } else { return nil } } - - @Option(name: .shortAndLong, help: "How to handle an existing swift-java.config; by default 'overwrite' by can be changed to amending a configuration") - var existingConfig: ExistingConfigFileMode = .overwrite - public enum ExistingConfigFileMode: String, ExpressibleByArgument, Codable { - case overwrite - case amend - } - - @Option(name: .shortAndLong, help: "While scanning a classpath, inspect only types included in this package") - var javaPackageFilter: String? = nil @Argument( help: "The input file, which is either a Java2Swift configuration file or (if '-jar' was specified) a Jar file." ) - var input: String? - - /// Whether we have ensured that the output directory exists. - var createdOutputDirectory: Bool = false - - var moduleBaseDir: Foundation.URL? { - if let outputDirectory { - if outputDirectory == "-" { - return nil - } - - print("[debug][swift-java] Module base directory based on outputDirectory!") - return URL(fileURLWithPath: outputDirectory) - } - - guard let moduleName else { - return nil - } - - // Put the result into Sources/\(moduleName). - let baseDir = URL(fileURLWithPath: ".") - .appendingPathComponent("Sources", isDirectory: true) - .appendingPathComponent(moduleName, isDirectory: true) - - return baseDir - } - - /// The output directory in which to place the generated files, which will - /// be the specified directory (--output-directory or -o option) if given, - /// or a default directory derived from the other command-line arguments. - /// - /// Returns `nil` only when we should emit the files to standard output. - var actualOutputDirectory: Foundation.URL? { - if let outputDirectory { - if outputDirectory == "-" { - return nil - } - - return URL(fileURLWithPath: outputDirectory) - } - - guard let moduleName else { - fatalError("--module-name must be set!") - } - - // Put the result into Sources/\(moduleName). - let baseDir = URL(fileURLWithPath: ".") - .appendingPathComponent("Sources", isDirectory: true) - .appendingPathComponent(moduleName, isDirectory: true) - - // For generated Swift sources, put them into a "generated" subdirectory. - // The configuration file goes at the top level. - let outputDir: Foundation.URL - if jar { - precondition(self.input != nil, "-jar mode requires path to jar to be specified as input path") - outputDir = baseDir - } else { - outputDir = baseDir - .appendingPathComponent("generated", isDirectory: true) - } - - return outputDir - } + var input: String? // FIXME: top level command cannot have input argument like this + // FIXME: this is subcommands /// Describes what kind of generation action is being performed by swift-java. enum ToolMode { - /// Generate a configuration file given a Jar file. - case configuration(extraClasspath: String) // FIXME: this is more like "extract" configuration from classpath + // /// Generate a configuration file given a Jar file. + // case configuration(extraClasspath: String) // FIXME: this is more like "extract" configuration from classpath /// Generate Swift wrappers for Java classes based on the given /// configuration. @@ -188,216 +112,180 @@ struct SwiftJava: AsyncParsableCommand { case jextract // TODO: carry jextract specific config here? } - mutating func run() async { - guard CommandLine.arguments.count > 1 else { + mutating func runSwiftJavaCommand(config: inout Configuration) async throws { + guard CommandLine.arguments.count > 2 else { // there's no "default" command, print USAGE when no arguments/parameters are passed. - print("Must specify run mode.\n\(Self.helpMessage())") + print("error: Must specify mode subcommand (e.g. configure, resolve, jextract, ...).\n\n\(Self.helpMessage())") return } - print("[info][swift-java] Run: \(CommandLine.arguments.joined(separator: " "))") - print("[info][swift-java] Current work directory: \(URL(fileURLWithPath: "."))") - print("[info][swift-java] Module base directory: \(moduleBaseDir)") - do { - var earlyConfig: Configuration? - if let moduleBaseDir { - print("[debug][swift-java] Load config from module base directory: \(moduleBaseDir.path)") - earlyConfig = try readConfiguration(sourceDir: moduleBaseDir.path) - } else if let inputSwift { - print("[debug][swift-java] Load config from module swift input directory: \(inputSwift)") - earlyConfig = try readConfiguration(sourceDir: inputSwift) - } - var config = earlyConfig ?? Configuration() + if let javaPackage { + config.javaPackage = javaPackage + } - config.logLevel = self.logLevel - if let javaPackage { - config.javaPackage = javaPackage + // Determine the mode in which we'll execute. + let toolMode: ToolMode + // TODO: some options are exclusive to each other so we should detect that + if let inputSwift = commonOptions.inputSwift { + guard let inputSwift = commonOptions.inputSwift else { + print("[swift-java] --input-swift enabled 'jextract' mode, however no --output-swift directory was provided!\n\(Self.helpMessage())") + return + } + guard let outputSwift else { + print("[swift-java] --output-swift enabled 'jextract' mode, however no --output-swift directory was provided!\n\(Self.helpMessage())") + return + } + guard let outputJava else { + print("[swift-java] --output-java enabled 'jextract' mode, however no --output-java directory was provided!\n\(Self.helpMessage())") + return + } + config.swiftModule = self.swiftModule ?? "UnknownModule" + config.inputSwiftDirectory = inputSwift + config.outputSwiftDirectory = outputSwift + config.outputJavaDirectory = outputJava + + toolMode = .jextract +// } else if jar { +// guard let input else { +// fatalError("Mode -jar requires path\n\(Self.helpMessage())") +// } +// toolMode = .configuration(extraClasspath: input) + } else if fetch { + guard let input else { + fatalError("Mode 'fetch' requires path\n\(Self.helpMessage())") } + config = try JavaTranslator.readConfiguration(from: URL(fileURLWithPath: input)) + guard let dependencies = config.dependencies else { + print("[swift-java] Running in 'fetch dependencies' mode but dependencies list was empty!") + print("[swift-java] Nothing to do: done.") + return + } + toolMode = .fetchDependencies + } else { + guard let input else { + fatalError("Mode -jar requires path\n\(Self.helpMessage())") + } + config = try JavaTranslator.readConfiguration(from: URL(fileURLWithPath: input)) + toolMode = .classWrappers + } - // Determine the mode in which we'll execute. - let toolMode: ToolMode - // TODO: some options are exclusive to each other so we should detect that - if let inputSwift { - guard let outputSwift else { - print("[swift-java] --input-swift enabled 'jextract' mode, however no --output-swift directory was provided!\n\(Self.helpMessage())") - return - } - guard let outputJava else { - print("[swift-java] --input-swift enabled 'jextract' mode, however no --output-java directory was provided!\n\(Self.helpMessage())") - return - } - config.swiftModule = self.moduleName // FIXME: rename the moduleName - config.inputSwiftDirectory = self.inputSwift - config.outputSwiftDirectory = self.outputSwift - config.outputJavaDirectory = self.outputJava - config.mode = self.mode - - toolMode = .jextract - } else if jar { - guard let input else { - fatalError("Mode -jar requires path\n\(Self.helpMessage())") - } - toolMode = .configuration(extraClasspath: input) - } else if fetch { - guard let input else { - fatalError("Mode 'fetch' requires path\n\(Self.helpMessage())") - } - config = try JavaTranslator.readConfiguration(from: URL(fileURLWithPath: input)) - guard let dependencies = config.dependencies else { - print("[swift-java] Running in 'fetch dependencies' mode but dependencies list was empty!") - print("[swift-java] Nothing to do: done.") - return - } - toolMode = .fetchDependencies - } else { - guard let input else { - fatalError("Mode -jar requires path\n\(Self.helpMessage())") - } - config = try JavaTranslator.readConfiguration(from: URL(fileURLWithPath: input)) - toolMode = .classWrappers + print("[debug][swift-java] Running swift-java in mode: " + "\(toolMode.prettyName)".bold) + + let swiftModule: String = + self.swiftModule ?? + self.effectiveSwiftModule.split(separator: "/").dropLast().last.map(String.init) ?? "__UnknownModule" + + // Load all of the dependent configurations and associate them with Swift + // modules. + let dependentConfigs = try dependsOn.map { dependentConfig in + guard let equalLoc = dependentConfig.firstIndex(of: "=") else { + throw JavaToSwiftError.badConfigOption(dependentConfig) } - print("[debug][swift-java] Running swift-java in mode: " + "\(toolMode.prettyName)".bold) + let afterEqual = dependentConfig.index(after: equalLoc) + let swiftModuleName = String(dependentConfig[.. (javaClassName: String, swiftName: String) { @@ -421,65 +309,6 @@ struct SwiftJava: AsyncParsableCommand { return (javaClassName, swiftName.javaClassNameToCanonicalName) } - mutating func writeContents( - _ contents: String, - to filename: String, description: String) throws { - try writeContents( - contents, - outputDirectoryOverride: self.actualOutputDirectory, - to: filename, - description: description) - } - - mutating func writeContents( - _ contents: String, - outputDirectoryOverride: Foundation.URL?, - to filename: String, - description: String) throws { - guard let outputDir = (outputDirectoryOverride ?? actualOutputDirectory) else { - print("// \(filename) - \(description)") - print(contents) - return - } - - // If we haven't tried to create the output directory yet, do so now before - // we write any files to it. - if !createdOutputDirectory { - try FileManager.default.createDirectory( - at: outputDir, - withIntermediateDirectories: true - ) - createdOutputDirectory = true - } - - // Write the file: - let file = outputDir.appendingPathComponent(filename) - print("[debug][swift-java] Writing \(description) to '\(file.path)'... ", terminator: "") - try contents.write(to: file, atomically: true, encoding: .utf8) - print("done.".green) - } -} - -extension SwiftJava { - /// Get base configuration, depending on if we are to 'amend' or 'overwrite' the existing configuration. - package func getBaseConfigurationForWrite() throws -> (Bool, Configuration) { - guard let actualOutputDirectory = self.actualOutputDirectory else { - // If output has no path there's nothing to amend - return (false, .init()) - } - - switch self.existingConfig { - case .overwrite: - // always make up a fresh instance if we're overwriting - return (false, .init()) - case .amend: - let configPath = actualOutputDirectory - guard let config = try readConfiguration(sourceDir: configPath.path) else { - return (false, .init()) - } - return (true, config) - } - } } enum JavaToSwiftError: Error { @@ -495,21 +324,9 @@ extension JavaToSwiftError: CustomStringConvertible { } } -@JavaClass("java.lang.ClassLoader") -public struct ClassLoader { - @JavaMethod - public func loadClass(_ arg0: String) throws -> JavaClass? -} - -extension JavaClass { - @JavaStaticMethod - public func getSystemClassLoader() -> ClassLoader? -} - extension SwiftJava.ToolMode { var prettyName: String { switch self { - case .configuration: "Configuration" case .fetchDependencies: "Fetch dependencies" case .classWrappers: "Wrap Java classes" case .jextract: "JExtract Swift for Java" diff --git a/Sources/SwiftJavaTool/SwiftJavaBaseAsyncParsableCommand.swift b/Sources/SwiftJavaTool/SwiftJavaBaseAsyncParsableCommand.swift new file mode 100644 index 00000000..afe4f80c --- /dev/null +++ b/Sources/SwiftJavaTool/SwiftJavaBaseAsyncParsableCommand.swift @@ -0,0 +1,181 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2024 Apple Inc. and the Swift.org project authors +// Licensed under Apache License v2.0 +// +// See LICENSE.txt for license information +// See CONTRIBUTORS.txt for the list of Swift.org project authors +// +// SPDX-License-Identifier: Apache-2.0 +// +//===----------------------------------------------------------------------===// + +import ArgumentParser +import Foundation +import SwiftJavaLib +import JExtractSwiftLib +import JavaKit +import JavaKitJar +import JavaKitNetwork +import JavaKitReflection +import SwiftSyntax +import SwiftSyntaxBuilder +import JavaKitConfigurationShared +import JavaKitShared + +protocol SwiftJavaBaseAsyncParsableCommand: AsyncParsableCommand { + var logLevel: Logger.Level { get set } + + var commonOptions: SwiftJava.CommonOptions { get set } + + var effectiveSwiftModule: String { get } + + mutating func runSwiftJavaCommand(config: inout Configuration) async throws + +} + +extension SwiftJavaBaseAsyncParsableCommand { + public mutating func run() async { + print("[info][swift-java] Run: \(CommandLine.arguments.joined(separator: " "))") + print("[info][swift-java] Current work directory: \(URL(fileURLWithPath: "."))") + + do { + var config = try readInitialConfiguration(command: self) + try await runSwiftJavaCommand(config: &config) + } catch { + // We fail like this since throwing out of the run often ends up hiding the failure reason when it is executed as SwiftPM plugin (!) + let message = "Failed with error: \(error)" + print("[error][java-swift] \(message)") + fatalError(message) + } + + // Just for debugging so it is clear which command has finished + print("[debug][swift-java] " + "Done: ".green + CommandLine.arguments.joined(separator: " ").green) + } +} + +extension SwiftJavaBaseAsyncParsableCommand { + mutating func writeContents( + _ contents: String, + to filename: String, description: String) throws { + try writeContents( + contents, + outputDirectoryOverride: self.actualOutputDirectory, + to: filename, + description: description) + } + + mutating func writeContents( + _ contents: String, + outputDirectoryOverride: Foundation.URL?, + to filename: String, + description: String) throws { + guard let outputDir = (outputDirectoryOverride ?? actualOutputDirectory) else { + print("// \(filename) - \(description)") + print(contents) + return + } + + // If we haven't tried to create the output directory yet, do so now before + // we write any files to it. + // if !createdOutputDirectory { + try FileManager.default.createDirectory( + at: outputDir, + withIntermediateDirectories: true + ) + // createdOutputDirectory = true + //} + + // Write the file: + let file = outputDir.appendingPathComponent(filename) + print("[debug][swift-java] Writing \(description) to '\(file.path)'... ", terminator: "") + try contents.write(to: file, atomically: true, encoding: .utf8) + print("done.".green) + } +} + + +extension SwiftJavaBaseAsyncParsableCommand { + var logLevel: Logger.Level { + get { + self.commonOptions.logLevel + } + set { + self.commonOptions.logLevel = newValue + } + } +} +extension SwiftJavaBaseAsyncParsableCommand { + + var moduleBaseDir: Foundation.URL? { +// if let outputDirectory = commonOptions.outputDirectory { +// if outputDirectory == "-" { +// return nil +// } +// +// print("[debug][swift-java] Module base directory based on outputDirectory!") +// return URL(fileURLWithPath: outputDirectory) +// } + +// guard let swiftModule else { +// return nil +// } + + // Put the result into Sources/\(swiftModule). + let baseDir = URL(fileURLWithPath: ".") + .appendingPathComponent("Sources", isDirectory: true) + .appendingPathComponent(self.effectiveSwiftModule, isDirectory: true) + + return baseDir + } + + /// The output directory in which to place the generated files, which will + /// be the specified directory (--output-directory or -o option) if given, + /// or a default directory derived from the other command-line arguments. + /// + /// Returns `nil` only when we should emit the files to standard output. + var actualOutputDirectory: Foundation.URL? { + if let outputDirectory = commonOptions.outputDirectory { + if outputDirectory == "-" { + return nil + } + + return URL(fileURLWithPath: outputDirectory) + } + + // Put the result into Sources/\(swiftModule). + let baseDir = URL(fileURLWithPath: ".") + .appendingPathComponent("Sources", isDirectory: true) + .appendingPathComponent(effectiveSwiftModule, isDirectory: true) + + // For generated Swift sources, put them into a "generated" subdirectory. + // The configuration file goes at the top level. + let outputDir: Foundation.URL + // if jar { + // precondition(self.input != nil, "-jar mode requires path to jar to be specified as input path") + outputDir = baseDir + // } else { + // outputDir = baseDir + // .appendingPathComponent("generated", isDirectory: true) + // } + + return outputDir + } + + func readInitialConfiguration(command: some SwiftJavaBaseAsyncParsableCommand) throws -> Configuration { + var earlyConfig: Configuration? + if let moduleBaseDir { + print("[debug][swift-java] Load config from module base directory: \(moduleBaseDir.path)") + earlyConfig = try readConfiguration(sourceDir: moduleBaseDir.path) + } else if let inputSwift = commonOptions.inputSwift { + print("[debug][swift-java] Load config from module swift input directory: \(inputSwift)") + earlyConfig = try readConfiguration(sourceDir: inputSwift) + } + var config = earlyConfig ?? Configuration() + // override configuration with options from command line + config.logLevel = command.logLevel + return config + } +} \ No newline at end of file diff --git a/USER_GUIDE.md b/USER_GUIDE.md index 2abe8ea0..d7d796fd 100644 --- a/USER_GUIDE.md +++ b/USER_GUIDE.md @@ -8,8 +8,8 @@ Before using this package, set the `JAVA_HOME` environment variable to point at ### Using Java libraries from Swift -Existing Java libraries can be wrapped for use in Swift with the `Java2Swift` -tool. In a Swift program, the most direct way to access a Java API is to use the SwiftPM plugin to provide Swift wrappers for the Java classes. To do so, add a configuration file `Java2Swift.config` into the source directory for the Swift target. This is a JSON file that specifies Java classes and the Swift type name that should be generated to wrap them. For example, the following file maps `java.math.BigInteger` to a Swift type named `BigInteger`: +Existing Java libraries can be wrapped for use in Swift with the `swift-java` +tool. In a Swift program, the most direct way to access a Java API is to use the SwiftPM plugin to provide Swift wrappers for the Java classes. To do so, add a configuration file `swift-java.config` into the source directory for the Swift target. This is a JSON file that specifies Java classes and the Swift type name that should be generated to wrap them. For example, the following file maps `java.math.BigInteger` to a Swift type named `BigInteger`: ```json { @@ -31,11 +31,11 @@ or, equivalently, adding the following to the package dependencies: .package(url: "https://github.com/swiftlang/swift-java", branch: "main"), ``` -Finally, update `Package.swift` so that the `Java2SwiftPlugin` plugin runs on the target in which you want to generate Swift wrappers. The plugin looks like this: +Finally, update `Package.swift` so that the `SwiftJavaPlugin` plugin runs on the target in which you want to generate Swift wrappers. The plugin looks like this: ```swift plugins: [ - .plugin(name: "Java2SwiftPlugin", package: "swift-java"), + .plugin(name: "SwiftJavaPlugin", package: "swift-java"), ] ``` @@ -101,10 +101,10 @@ let bigInt = BigInteger(veryBigNumber, environment: jniEnvironment) ### Importing a Jar file into Swift -Java libraries are often distributed as Jar files. The `Java2Swift` tool can inspect a Jar file to create a `Java2Swift.config` file that will wrap all of the public classes for use in Swift. Following the example in `swift-java/Samples/JavaSieve`, we will wrap a small [Java library for computing prime numbers](https://github.com/gazman-sdk/quadratic-sieve-Java) for use in Swift. Assuming we have a Jar file `QuadraticSieve-1.0.jar` in the package directory, run the following command: +Java libraries are often distributed as Jar files. The `swift-java` tool can inspect a Jar file to create a `swift-java.config` file that will wrap all of the public classes for use in Swift. Following the example in `swift-java/Samples/JavaSieve`, we will wrap a small [Java library for computing prime numbers](https://github.com/gazman-sdk/quadratic-sieve-Java) for use in Swift. Assuming we have a Jar file `QuadraticSieve-1.0.jar` in the package directory, run the following command: ```swift -swift run Java2Swift --module-name JavaSieve --jar QuadraticSieve-1.0.jar +swift-java generate --module-name JavaSieve --jar QuadraticSieve-1.0.jar ``` The resulting configuration file will look something like this: @@ -142,7 +142,7 @@ The resulting configuration file will look something like this: } ``` -As with the previous `JavaProbablyPrime` sample, the `JavaSieve` target in `Package.swift` should depend on the `swift-java` package modules (`JavaKit`) and apply the `Java2Swift` plugin. This makes all of the Java classes found in the Jar file available to Swift within the `JavaSieve` target. +As with the previous `JavaProbablyPrime` sample, the `JavaSieve` target in `Package.swift` should depend on the `swift-java` package modules (`JavaKit`) and apply the `swift-java` plugin. This makes all of the Java classes found in the Jar file available to Swift within the `JavaSieve` target. If you inspect the build output, there are a number of warnings that look like this: @@ -159,12 +159,12 @@ These warnings mean that some of the APIs in the Java library aren't available i .product(name: "JavaKit", package: "swift-java"), ], plugins: [ - .plugin(name: "Java2SwiftPlugin", package: "swift-java"), + .plugin(name: "SwiftJavaPlugin", package: "swift-java"), ] ), ``` -Then define a a Java2Swift configuration file in `Sources/JavaMath/Java2Swift.config` to bring in the types we need: +Then define a a swift-java configuration file in `Sources/JavaMath/swift-java.config` to bring in the types we need: ```json { @@ -255,7 +255,7 @@ public class HelloSwift { } ``` -On the Swift side, the Java class needs to be exposed to Swift through `Java2Swift.config`, e.g.,: +On the Swift side, the Java class needs to be exposed to Swift through `swift-java.config`, e.g.,: ```swift { @@ -393,7 +393,7 @@ A number of JavaKit modules provide Swift projections of Java classes and interf | `java.lang.Throwable` | `Throwable` | `JavaKit` | | `java.net.URL` | `URL` | `JavaKitNetwork` | -The `Java2Swift` tool can translate any other Java classes into Swift projections. The easiest way to use `Java2Swift` is with the SwiftPM plugin described above. More information about using this tool directly are provided later in this document +The `swift-java` tool can translate any other Java classes into Swift projections. The easiest way to use `swift-java` is with the SwiftPM plugin described above. More information about using this tool directly are provided later in this document #### Improve parameter names of imported Java methods When building Java libraries you can pass the `-parameters` option to javac @@ -438,65 +438,71 @@ public struct Enumeration { } ``` -## Translating Java classes with `Java2Swift` +## Translating Java classes with `swift-java` -The `Java2Swift` is a Swift program that uses Java's runtime reflection facilities to translate the requested Java classes into their Swift projections. The output is a number of Swift source files, each of which corresponds to a -single Java class. The `Java2Swift` can be executed like this: +The `swift-java` is a Swift program that uses Java's runtime reflection facilities to translate the requested Java classes into their Swift projections. The output is a number of Swift source files, each of which corresponds to a +single Java class. The `swift-java` can be executed like this: ``` -swift run Java2Swift +swift-java ``` to produce help output like the following: ``` -USAGE: Java2Swift --module-name [--depends-on ...] [--jar] [--cp ...] [--output-directory ] +OVERVIEW: Generate sources and configuration for Swift and Java interoperability. -ARGUMENTS: - The input file, which is either a Java2Swift - configuration file or (if '-jar' was specified) - a Jar file. +USAGE: swift-java OPTIONS: - --module-name - The name of the Swift module into which the resulting - Swift types will be generated. --depends-on - A Java2Swift configuration file for a given Swift - module name on which this module depends, e.g., - JavaKitJar=Sources/JavaKitJar/Java2Swift.config. - There should be one of these options for each Swift - module that this module depends on (transitively) - that contains wrapped Java sources. - --jar Specifies that the input is a Jar file whose public - classes will be loaded. The output of Java2Swift will - be a configuration file (Java2Swift.config) that can - be used as input to a subsequent Java2Swift - invocation to generate wrappers for those public - classes. - --cp, --classpath Class search path of directories and zip/jar files - from which Java classes can be loaded. + A Java2Swift configuration file for a given Swift module name on which this module depends, e.g., JavaKitJar=Sources/JavaKitJar/Java2Swift.config. There should be one of these options for each Swift module that this + module depends on (transitively) that contains wrapped Java sources. + --jar Specifies that the input is a Jar file whose public classes will be loaded. The output of Java2Swift will be a configuration file (Java2Swift.config) that can be used as input to a subsequent Java2Swift invocation to + generate wrappers for those public classes. + --fetch Fetch dependencies from given target (containing swift-java configuration) or dependency string + --swift-native-implementation + The names of Java classes whose declared native methods will be implemented in Swift. + --output-swift + The directory where generated Swift files should be written. Generally used with jextract mode. + --output-java + The directory where generated Java files should be written. Generally used with jextract mode. + --java-package + The Java package the generated Java code should be emitted into. + -c, --cache-directory + Directory where to write cached values (e.g. swift-java.classpath files) -o, --output-directory - The directory in which to output the generated Swift - files or the Java2Swift configuration file. (default: - .) + The directory in which to output the generated Swift files or the SwiftJava configuration file. + --input-swift + Directory containing Swift files which should be extracted into Java bindings. Also known as 'jextract' mode. Must be paired with --output-java and --output-swift. + -l, --log-level + Configure the level of logs that should be printed (values: trace, debug, info, notice, warning, error, critical; default: log level) + --cp, --classpath Class search path of directories and zip/jar files from which Java classes can be loaded. + -f, --filter-java-package + While scanning a classpath, inspect only types included in this package -h, --help Show help information. + +SUBCOMMANDS: + configure Configure and emit a swift-java.config file based on an input dependency or jar file + resolve Resolve dependencies and write the resulting swift-java.classpath file + + See 'swift-java help ' for detailed help. ``` For example, the `JavaKitJar` library is generated with this command line: ```swift -swift run Java2Swift --module-name JavaKitJar --depends-on JavaKit=Sources/JavaKit/Java2Swift.config -o Sources/JavaKitJar/generated Sources/JavaKitJar/Java2Swift.config +swift run swift-java --module-name JavaKitJar --depends-on JavaKit=Sources/JavaKit/swift-java.config -o Sources/JavaKitJar/generated Sources/JavaKitJar/swift-java.config ``` -The `--module-name JavaKitJar` parameter describes the name of the Swift module in which the code will be generated. +The `--swift-module JavaKitJar` parameter describes the name of the Swift module in which the code will be generated. -The `--depends-on` option is followed by the Java2Swift configuration files for any library on which this Swift library depends. Each `--depends-on` option is of the form `=`, and tells Java2Swift which other Java classes have already been translated to Swift. For example, if your Java class uses `java.net.URL`, then you should include +The `--depends-on` option is followed by the swift-java configuration files for any library on which this Swift library depends. Each `--depends-on` option is of the form `=`, and tells swift-java which other Java classes have already been translated to Swift. For example, if your Java class uses `java.net.URL`, then you should include `JavaKitNetwork`'s configuration file as a dependency here. The `-o` option specifies the output directory. Typically, this will be `Sources//generated` or similar to keep the generated Swift files separate from any hand-written ones. To see the output on the terminal rather than writing files to disk, pass `-` for this option. -Finally, the command line should contain the `Java2Swift.config` file containing the list of classes that should be translated into Swift and their corresponding Swift type names. The tool will output a single `.swift` file for each class, along with warnings for any public API that cannot be translated into Swift. The most common warnings are due to missing Swift projections for Java classes. For example, here we have not translated (or provided the translation manifests for) the Java classes +Finally, the command line should contain the `swift-java.config` file containing the list of classes that should be translated into Swift and their corresponding Swift type names. The tool will output a single `.swift` file for each class, along with warnings for any public API that cannot be translated into Swift. The most common warnings are due to missing Swift projections for Java classes. For example, here we have not translated (or provided the translation manifests for) the Java classes `java.util.zip.ZipOutputStream` and `java.io.OutputStream`: ``` @@ -507,7 +513,7 @@ warning: Unable to translate 'java.util.jar.JarInputStream' method 'transferTo': The result of such warnings is that certain information won't be statically available in Swift, e.g., the superclass won't be known (so we will assume it is `JavaObject`), or the specified constructors or methods won't be translated. If you don't need these APIs, the warnings can be safely ignored. The APIs can still be called dynamically via JNI. -The `--jar` option changes the operation of `Java2Swift`. Instead of wrapping Java classes in Swift, it scans the given input Jar file to find all public classes and outputs a configuration file `Java2Swift.config` mapping all of the Java classes in the Jar file to Swift types. The `--jar` mode is expected to be used to help import a Java library into Swift wholesale, after which Java2Swift should invoked again given the generated configuration file. +The `--jar` option changes the operation of `swift-java`. Instead of wrapping Java classes in Swift, it scans the given input Jar file to find all public classes and outputs a configuration file `swift-java.config` mapping all of the Java classes in the Jar file to Swift types. The `--jar` mode is expected to be used to help import a Java library into Swift wholesale, after which swift-java should invoked again given the generated configuration file. ### Under construction: Create a Java class to wrap the Swift library @@ -651,6 +657,7 @@ A Swift function may accept a closure which is used as a callback: func callMe(maybe: () -> ()) {} ``` +Minimal support for c-compatible closures is implemented, more documentation soon. ## `jextract-swift` importer behavior