diff --git a/modules/bindgen/src/main/scala/Config.scala b/modules/bindgen/src/main/scala/Config.scala index 61249890..c1548d41 100644 --- a/modules/bindgen/src/main/scala/Config.scala +++ b/modules/bindgen/src/main/scala/Config.scala @@ -25,7 +25,8 @@ case class Config( printFiles: PrintFiles, exportMode: ExportMode, outputChannel: OutputChannel, - tempDir: TempPath + tempDir: TempPath, + excludeSystemPaths: List[SystemPath] ) case class Context( @@ -35,19 +36,11 @@ case class Context( ) object Config: - def withDefaults( - // headerFile: HeaderFile, - // packageName: PackageName, - // lang: Lang, - // outputChannel: OutputChannel - ) = + def withDefaults() = Config( - // packageName = packageName, - // headerFile = headerFile, linkName = None, indentSize = defaults.indentSize, indents = defaults.indents, - // lang = lang, cImports = Nil, clangFlags = Nil, quiet = Quiet.No, @@ -59,7 +52,8 @@ object Config: printFiles = PrintFiles.No, exportMode = ExportMode.No, outputChannel = OutputChannel.cli, - tempDir = TempPath(sys.props("java.io.tmpdir")) + tempDir = TempPath(sys.props("java.io.tmpdir")), + excludeSystemPaths = Nil ) object defaults: val indentSize = IndentationSize(3) @@ -107,6 +101,9 @@ object CImport extends OpaqueString[CImport] opaque type ClangFlag = String object ClangFlag extends OpaqueString[ClangFlag] +opaque type SystemPath = String +object SystemPath extends OpaqueString[SystemPath] + opaque type HeaderFile = String object HeaderFile extends OpaqueString[HeaderFile] diff --git a/modules/bindgen/src/main/scala/analysis/InteractiveDriver.scala b/modules/bindgen/src/main/scala/analysis/InteractiveDriver.scala index 9413ddb6..fec42e88 100644 --- a/modules/bindgen/src/main/scala/analysis/InteractiveDriver.scala +++ b/modules/bindgen/src/main/scala/analysis/InteractiveDriver.scala @@ -34,7 +34,10 @@ object InteractiveDriver: Right( InteractiveDriver( config, - ConfiguredEnvironment(clang, SystemHeaderDetector(clang)) + ConfiguredEnvironment( + clang, + SystemHeaderDetector(clang, config.excludeSystemPaths) + ) ) ) end InteractiveDriver diff --git a/modules/bindgen/src/main/scala/analysis/SystemHeaderDetector.scala b/modules/bindgen/src/main/scala/analysis/SystemHeaderDetector.scala index 50823177..11b026cc 100644 --- a/modules/bindgen/src/main/scala/analysis/SystemHeaderDetector.scala +++ b/modules/bindgen/src/main/scala/analysis/SystemHeaderDetector.scala @@ -13,16 +13,28 @@ import scala.scalanative.unsigned.* import scalanative.libc.* import scala.scalanative.runtime.libc import libclang.fluent.string +import java.nio.file.Path +import java.nio.file.Paths -class SystemHeaderDetector(clangInfo: ClangInfo): +class SystemHeaderDetector( + clangInfo: ClangInfo, + excludeSystemPaths: List[SystemPath] +): private val mut = collection.mutable.Map.empty[String, Boolean] + private val includes = clangInfo.includePaths.map(Paths.get(_)) + private val excludes = excludeSystemPaths.map(_.value).map(Paths.get(_)) def isSystem(filename: String): Boolean = val path = java.nio.file.Paths.get(filename) + + def isChild(ip: Path) = path.startsWith(ip) + + val isInExcludedLocation = excludes.exists(isChild) + val isInSystemLocation = includes.exists(isChild) + mut.getOrElseUpdate( filename, - clangInfo.includePaths.exists { ip => - path.startsWith(ip) - } + if isInExcludedLocation then false else isInSystemLocation ) + end isSystem end SystemHeaderDetector diff --git a/modules/bindgen/src/main/scala/cli/arguments.scala b/modules/bindgen/src/main/scala/cli/arguments.scala index 67c6495a..519a830d 100644 --- a/modules/bindgen/src/main/scala/cli/arguments.scala +++ b/modules/bindgen/src/main/scala/cli/arguments.scala @@ -121,6 +121,16 @@ object CLI: .withDefault(Nil) .map(_.map(ClangFlag.apply(_))) + private val excludeSystemPaths = Opts + .options[String]( + "exclude-system-path", + help = + "List of paths to mark as non-system (helpful if Clang reports some paths you'd rather not ignore)" + ) + .map(_.toList) + .withDefault(Nil) + .map(_.map(SystemPath.apply(_))) + private val clangInclude = Opts .options[String]( "clang-include", @@ -348,7 +358,8 @@ object CLI: printFiles, exportMode, Opts(OutputChannel.cli), - tempDir + tempDir, + excludeSystemPaths ).mapN(Config.apply) val command = Command( diff --git a/modules/bindgen/src/main/scala/main.scala b/modules/bindgen/src/main/scala/main.scala index e4b4c5c7..9dbf06e1 100644 --- a/modules/bindgen/src/main/scala/main.scala +++ b/modules/bindgen/src/main/scala/main.scala @@ -44,28 +44,34 @@ object Generate: config.config.outputChannel.stdoutLine(lb.result) case (RenderedOutput.Single(lb), OutputMode.SingleFile(f)) => - Using.resource(new FileWriter(f.value)) { fw => - fw.write(lb.result) - } - info(s"Generated ${f.value.toPath.toAbsolutePath()}") + val result = lb.result.trim() + if result.nonEmpty then + Using.resource(new FileWriter(f.value)) { fw => + fw.write(result) + } + info(s"Generated ${f.value.toPath.toAbsolutePath()}") - if config.config.printFiles == PrintFiles.Yes then - config.config.outputChannel.stdoutLine( - f.value.toPath.toAbsolutePath().toString() - ) + if config.config.printFiles == PrintFiles.Yes then + config.config.outputChannel.stdoutLine( + f.value.toPath.toAbsolutePath().toString() + ) + end if case (RenderedOutput.Multi(mp), OutputMode.MultiFile(d)) => val path = d.value.toPath() mp.foreach { (sn, lb) => - val file = path.resolve(sn.value + ".scala") - Using.resource(new FileWriter(file.toFile)) { fw => - fw.write(lb.result) - } - info(s"Generated ${file.toAbsolutePath()}") - if config.config.printFiles == PrintFiles.Yes then - config.config.outputChannel.stdoutLine( - file.toAbsolutePath() - ) + val result = lb.result.trim() + if result.nonEmpty then + val file = path.resolve(sn.value + ".scala") + Using.resource(new FileWriter(file.toFile)) { fw => + fw.write(result) + } + info(s"Generated ${file.toAbsolutePath()}") + if config.config.printFiles == PrintFiles.Yes then + config.config.outputChannel.stdoutLine( + file.toAbsolutePath() + ) + end if } end match diff --git a/modules/bindgen/src/main/scala/render/functionRewriter.scala b/modules/bindgen/src/main/scala/render/functionRewriter.scala index 5728015d..1f2be060 100644 --- a/modules/bindgen/src/main/scala/render/functionRewriter.scala +++ b/modules/bindgen/src/main/scala/render/functionRewriter.scala @@ -104,7 +104,7 @@ enum GeneratedFunction: end GeneratedFunction private def externFuncName(s: String)(using c: Context) = - s"__sn_wrap_${c.packageName.value}_" + s + s"__sn_wrap_${c.packageName.value.replace(".", "_")}_" + s private def scalaForwarderFunction( bad: Def.Function diff --git a/modules/bindgen/src/main/scala/render/scalaType.scala b/modules/bindgen/src/main/scala/render/scalaType.scala index a5bc053f..9eb5a639 100644 --- a/modules/bindgen/src/main/scala/render/scalaType.scala +++ b/modules/bindgen/src/main/scala/render/scalaType.scala @@ -11,7 +11,7 @@ def renderName(name: Name.Model)(using config: Config) = (nameMatches orElse pathMatches) .map(_._2.value) .map { pkgName => - s"_root_.$pkgName.all.${name.value}" + s"_root_.$pkgName.${name.value}" } .getOrElse(name.value) end renderName diff --git a/modules/interface/src/main/scala/Interface.scala b/modules/interface/src/main/scala/Interface.scala index 20176a48..32356be1 100644 --- a/modules/interface/src/main/scala/Interface.scala +++ b/modules/interface/src/main/scala/Interface.scala @@ -84,6 +84,7 @@ class Binding private ( val externalPaths: Map[String, String] = Defaults.externalPaths, val externalNames: Map[String, String] = Defaults.externalNames, val bindgenArguments: List[String] = Defaults.bindgenArguments, + val excludeSystemPaths: List[Path] = Defaults.excludeSystemPaths, val scalaFile: String, val cFile: String ) { self => @@ -106,6 +107,7 @@ class Binding private ( externalPaths: Map[String, String] = self.externalPaths, externalNames: Map[String, String] = self.externalNames, bindgenArguments: List[String] = self.bindgenArguments, + excludeSystemPaths: List[Path] = self.excludeSystemPaths, scalaFile: String = self.scalaFile, cFile: String = self.cFile ) = @@ -127,6 +129,7 @@ class Binding private ( externalPaths = externalPaths, externalNames = externalNames, bindgenArguments = bindgenArguments, + excludeSystemPaths = excludeSystemPaths, scalaFile = scalaFile, cFile = cFile ) @@ -185,6 +188,10 @@ class Binding private ( arg("render.external-name", s"$filter=$pkg") } + excludeSystemPaths.map { case path => + arg("exclude-system-path", path.toString()) + } + sb ++= bindgenArguments sb.result() @@ -260,6 +267,12 @@ object Binding { def addExternalNames(externals: Map[String, String]) = copy(b => b.copy(externalNames = b.externalNames ++ externals)) + def addExcludedSystemPath(path: Path) = + copy(b => b.copy(excludeSystemPaths = b.excludeSystemPaths :+ path)) + + def withExcludedSystemPaths(paths: List[Path]) = + copy(b => b.copy(excludeSystemPaths = paths)) + def withBindgenArguments(arguments: List[String]) = copy(_.copy(bindgenArguments = arguments)) def addBindgenArgument(argument: String) = @@ -285,6 +298,7 @@ object Binding { val externalPaths = Map.empty[String, String] val externalNames = Map.empty[String, String] val bindgenArguments = List.empty[String] + val excludeSystemPaths = List.empty[Path] val exportMode = false } diff --git a/modules/sbt-plugin/src/sbt-test/basic/artifacts/test b/modules/sbt-plugin/src/sbt-test/basic/artifacts/test index 8fc8d2e0..f4661bef 100644 --- a/modules/sbt-plugin/src/sbt-test/basic/artifacts/test +++ b/modules/sbt-plugin/src/sbt-test/basic/artifacts/test @@ -1,8 +1,8 @@ > run -$ exists target/scala-3.1.1/resource_managed/main/scala-native/libtest.c +-$ exists target/scala-3.1.1/resource_managed/main/scala-native/libtest.c $ exists target/scala-3.1.1/src_managed/main/libtest.scala > test -$ exists target/scala-3.1.1/resource_managed/test/scala-native/gentests.c +-$ exists target/scala-3.1.1/resource_managed/test/scala-native/gentests.c $ exists target/scala-3.1.1/src_managed/test/gentests.scala diff --git a/modules/sbt-plugin/src/sbt-test/basic/binding-mode/build.sbt b/modules/sbt-plugin/src/sbt-test/basic/binding-mode/build.sbt index a001d0b5..0a7238b0 100644 --- a/modules/sbt-plugin/src/sbt-test/basic/binding-mode/build.sbt +++ b/modules/sbt-plugin/src/sbt-test/basic/binding-mode/build.sbt @@ -3,25 +3,32 @@ enablePlugins(BindgenPlugin, ScalaNativePlugin, ScalaNativeJUnitPlugin) import bindgen.interface.Binding import java.util.concurrent.atomic.AtomicReference -scalaVersion := "3.2.0" +scalaVersion := "3.3.1" bindgenBindings := { Seq( - Binding( - headerFile = + Binding + .builder( baseDirectory.value / "src" / "main" / "resources" / "scala-native" / "header.h", - packageName = "bindings" - ) + "bindings" + ) + .addCImport("header.h") + .build ) } Test / bindgenBindings := { Seq( - Binding( - headerFile = + Binding + .builder( baseDirectory.value / "src" / "main" / "resources" / "scala-native" / "header.h", - packageName = "testbindings" - ) + "testbindings" + ) + .addCImport("header.h") + .addClangFlag( + "-I" + (baseDirectory.value / "src" / "main" / "resources" / "scala-native").toString + ) + .build ) } @@ -40,3 +47,10 @@ Test / bindgenMode := { BindgenMode.Manual(scalaFolder, cFolder) } + +Test / nativeConfig := { + val config = (Test / nativeConfig).value + config.withCompileOptions( + config.compileOptions :+ ("-I" + (baseDirectory.value / "src" / "main" / "resources" / "scala-native").toString) + ) +} diff --git a/modules/sbt-plugin/src/sbt-test/basic/binding-mode/project/plugins.sbt b/modules/sbt-plugin/src/sbt-test/basic/binding-mode/project/plugins.sbt index 08d68fcf..a98839d8 100644 --- a/modules/sbt-plugin/src/sbt-test/basic/binding-mode/project/plugins.sbt +++ b/modules/sbt-plugin/src/sbt-test/basic/binding-mode/project/plugins.sbt @@ -1,4 +1,4 @@ addSbtPlugin( "com.indoorvivants" % "bindgen-sbt-plugin" % sys.props("plugin.version") ) -addSbtPlugin("org.scala-native" % "sbt-scala-native" % "0.4.7") +addSbtPlugin("org.scala-native" % "sbt-scala-native" % "0.4.16") diff --git a/modules/sbt-plugin/src/sbt-test/basic/binding-mode/src/main/resources/scala-native/header.h b/modules/sbt-plugin/src/sbt-test/basic/binding-mode/src/main/resources/scala-native/header.h index 26818b7f..f76b6359 100644 --- a/modules/sbt-plugin/src/sbt-test/basic/binding-mode/src/main/resources/scala-native/header.h +++ b/modules/sbt-plugin/src/sbt-test/basic/binding-mode/src/main/resources/scala-native/header.h @@ -1,2 +1,8 @@ #include + +typedef struct { + int i; +} mystruct; + +bool hello2(int i, mystruct y); bool hello(int i, float y); diff --git a/modules/sbt-plugin/src/sbt-test/basic/binding-mode/src/main/resources/scala-native/lib.c b/modules/sbt-plugin/src/sbt-test/basic/binding-mode/src/main/resources/scala-native/lib.c index 724f5234..4a99aa55 100644 --- a/modules/sbt-plugin/src/sbt-test/basic/binding-mode/src/main/resources/scala-native/lib.c +++ b/modules/sbt-plugin/src/sbt-test/basic/binding-mode/src/main/resources/scala-native/lib.c @@ -5,3 +5,8 @@ bool hello(int i, float y) { printf("%f", i * y); return true; } + +bool hello2(int i, mystruct y) { + printf("%d", i * ((int)y.i)); + return true; +} diff --git a/modules/sbt-plugin/src/sbt-test/basic/multi-file/build.sbt b/modules/sbt-plugin/src/sbt-test/basic/multi-file/build.sbt index 67ff0734..714d7daa 100644 --- a/modules/sbt-plugin/src/sbt-test/basic/multi-file/build.sbt +++ b/modules/sbt-plugin/src/sbt-test/basic/multi-file/build.sbt @@ -3,15 +3,17 @@ enablePlugins(BindgenPlugin, ScalaNativePlugin, ScalaNativeJUnitPlugin) import bindgen.interface.Binding import java.util.concurrent.atomic.AtomicReference -scalaVersion := "3.2.2" +scalaVersion := "3.3.1" bindgenBindings := { Seq( - Binding( - headerFile = + Binding + .builder( baseDirectory.value / "src" / "main" / "resources" / "scala-native" / "header.h", - packageName = "bindings", - multiFile = true - ) + "bindings" + ) + .withMultiFile(true) + .addCImport("header.h") + .build ) } diff --git a/modules/sbt-plugin/src/sbt-test/basic/multi-file/project/plugins.sbt b/modules/sbt-plugin/src/sbt-test/basic/multi-file/project/plugins.sbt index 08d68fcf..a98839d8 100644 --- a/modules/sbt-plugin/src/sbt-test/basic/multi-file/project/plugins.sbt +++ b/modules/sbt-plugin/src/sbt-test/basic/multi-file/project/plugins.sbt @@ -1,4 +1,4 @@ addSbtPlugin( "com.indoorvivants" % "bindgen-sbt-plugin" % sys.props("plugin.version") ) -addSbtPlugin("org.scala-native" % "sbt-scala-native" % "0.4.7") +addSbtPlugin("org.scala-native" % "sbt-scala-native" % "0.4.16") diff --git a/modules/sbt-plugin/src/sbt-test/basic/multi-file/src/main/resources/scala-native/header.h b/modules/sbt-plugin/src/sbt-test/basic/multi-file/src/main/resources/scala-native/header.h index 3191b5cf..95a355e9 100644 --- a/modules/sbt-plugin/src/sbt-test/basic/multi-file/src/main/resources/scala-native/header.h +++ b/modules/sbt-plugin/src/sbt-test/basic/multi-file/src/main/resources/scala-native/header.h @@ -3,12 +3,14 @@ bool hello(int i, float y); typedef float position; -struct Test { +typedef struct { char x; -}; +} Test; union Bla { char x; }; enum X { A, B }; + +bool hello2(int i, Test y); diff --git a/modules/sbt-plugin/src/sbt-test/basic/multi-file/src/main/resources/scala-native/lib.c b/modules/sbt-plugin/src/sbt-test/basic/multi-file/src/main/resources/scala-native/lib.c index 724f5234..9c2de9d0 100644 --- a/modules/sbt-plugin/src/sbt-test/basic/multi-file/src/main/resources/scala-native/lib.c +++ b/modules/sbt-plugin/src/sbt-test/basic/multi-file/src/main/resources/scala-native/lib.c @@ -5,3 +5,8 @@ bool hello(int i, float y) { printf("%f", i * y); return true; } + +bool hello2(int i, Test y) { + printf("%d", i * ((int)y.x)); + return true; +} diff --git a/modules/sbt-plugin/src/sbt-test/basic/multi-file/test b/modules/sbt-plugin/src/sbt-test/basic/multi-file/test index 28b48fd3..1e2542ca 100644 --- a/modules/sbt-plugin/src/sbt-test/basic/multi-file/test +++ b/modules/sbt-plugin/src/sbt-test/basic/multi-file/test @@ -1,7 +1,7 @@ > run -$ exists target/scala-3.2.2/resource_managed/main/scala-native/bindings.c -$ exists target/scala-3.2.2/src_managed/main/bindings/aliases.scala -$ exists target/scala-3.2.2/src_managed/main/bindings/enumerations.scala -$ exists target/scala-3.2.2/src_managed/main/bindings/structs.scala -$ exists target/scala-3.2.2/src_managed/main/bindings/unions.scala -$ exists target/scala-3.2.2/src_managed/main/bindings/functions.scala +$ exists target/scala-3.3.1/resource_managed/main/scala-native/bindings.c +$ exists target/scala-3.3.1/src_managed/main/bindings/aliases.scala +$ exists target/scala-3.3.1/src_managed/main/bindings/enumerations.scala +$ exists target/scala-3.3.1/src_managed/main/bindings/structs.scala +$ exists target/scala-3.3.1/src_managed/main/bindings/unions.scala +$ exists target/scala-3.3.1/src_managed/main/bindings/functions.scala