diff --git a/core/src/fr/hammons/slinc/LibraryI.scala b/core/src/fr/hammons/slinc/LibraryI.scala index c1c1a4f1..011d92e1 100644 --- a/core/src/fr/hammons/slinc/LibraryI.scala +++ b/core/src/fr/hammons/slinc/LibraryI.scala @@ -33,6 +33,7 @@ object LibraryI: def getLocalLookup(name: String): Lookup def getLibraryPathLookup(name: String): Lookup def getStandardLibLookup: Lookup + def getResourceLibLookup(location: String): Lookup def checkMethodIsCompatible(using q: Quotes)(s: q.reflect.Symbol): Unit = import quotes.reflect.* @@ -237,20 +238,15 @@ object LibraryI: platformSpecificExpr: Expr[PlatformSpecific] )(using Quotes, Type[L]) = import quotes.reflect.* - val name: Option[LibraryLocation] = LibraryName.libraryName[L] + val name: LibraryLocation = LibraryName.libraryName[L] name match - case None => '{ $platformSpecificExpr.getStandardLibLookup } - case Some(LibraryLocation.Local(s)) => + case LibraryLocation.Standardard => '{ $platformSpecificExpr.getStandardLibLookup } + case LibraryLocation.Local(s) => '{ $platformSpecificExpr.getLocalLookup(${ Expr(s) }) } - case Some(LibraryLocation.Path(s)) => + case LibraryLocation.Path(s) => '{ $platformSpecificExpr.getLibraryPathLookup(${ Expr(s) }) } - case Some(LibraryLocation.Resource(s)) => - val sExpr = Expr(s) - val clPath = '{ - Tools.sendResourceToCache($sExpr) - Tools.compileCachedResourceIfNeeded($sExpr).toAbsolutePath().nn.toString() - } - '{ $platformSpecificExpr.getLocalLookup($clPath ) } + case LibraryLocation.Resource(s) => + '{ $platformSpecificExpr.getResourceLibLookup(${Expr(s)} ) } inline def getMethodAddress[L](l: Lookup) = ${ getMethodAddressImpl[L]('l) diff --git a/core/src/fr/hammons/slinc/LibraryName.scala b/core/src/fr/hammons/slinc/LibraryName.scala index c80fb55c..a08afc54 100644 --- a/core/src/fr/hammons/slinc/LibraryName.scala +++ b/core/src/fr/hammons/slinc/LibraryName.scala @@ -9,6 +9,7 @@ enum LibraryLocation: case Local(s: String) case Resource(s: String) case Path(s: String) + case Standardard object LibraryName: def libraryName[L](using Quotes, Type[L]) = @@ -24,4 +25,4 @@ object LibraryName: case s"@$path" => LibraryLocation.Resource(path) case s"#$path" => LibraryLocation.Path(path) case s => LibraryLocation.Local(s) - }.headOption + }.headOption.getOrElse(LibraryLocation.Standardard) diff --git a/core/src/fr/hammons/slinc/Lookup.scala b/core/src/fr/hammons/slinc/Lookup.scala index 40bd7f7c..f0bb5341 100644 --- a/core/src/fr/hammons/slinc/Lookup.scala +++ b/core/src/fr/hammons/slinc/Lookup.scala @@ -1,4 +1,17 @@ package fr.hammons.slinc -private[slinc] trait Lookup: +import java.nio.file.Path +import java.nio.file.Paths + +trait Lookup(libraryLocation: LibraryLocation): def lookup(name: String): Object + + def lookupError(name: String): Error = libraryLocation match + case LibraryLocation.Standardard => Error(s"Failed to load symbol $name from the standard library.") + case LibraryLocation.Resource(location) => + Error(s"Failed to load symbol $name from resource $location. This could be caused by resource collision. Is the resource name unique enough?") + case LibraryLocation.Local(location) => + val absPath = Paths.get(location).nn.toAbsolutePath.nn + Error(s"Failed to load symbol $name from local path $absPath") + case LibraryLocation.Path(libName) => + Error(s"Failed to load symbol $name from library $libName") \ No newline at end of file diff --git a/core/src/fr/hammons/slinc/Tools.scala b/core/src/fr/hammons/slinc/Tools.scala index 28ee6627..e861fa7f 100644 --- a/core/src/fr/hammons/slinc/Tools.scala +++ b/core/src/fr/hammons/slinc/Tools.scala @@ -11,42 +11,55 @@ import java.nio.channels.ByteChannel import java.nio.channels.Channels import scala.sys.process.* - -object Tools { +object Tools: private val appDataStore = - Paths.get(os match - case OS.Windows => - s"${System.getenv("APPDATA").nn}\\local\\slinc\\libstore\\" - case OS.Linux => s"${System.getProperty("user.home").nn}/.cache/slinc/" - case OS.Darwin => - s"${System.getProperty("user.home").nn}/Library/Application Support/" - ).nn - - private val sharedLibSuffix = + Paths + .get(os match + case OS.Windows => + s"${System.getenv("APPDATA").nn}\\local\\slinc\\libstore\\" + case OS.Linux => s"${System.getProperty("user.home").nn}/.cache/slinc/" + case OS.Darwin => + s"${System.getProperty("user.home").nn}/Library/Application Support/" + ) + .nn + + private val sharedLibSuffix = os match case OS.Linux | OS.Darwin => ".so" - case OS.Windows => ".dll" - case OS.Unknown => throw Error("Cannot do lib compilation on this platform, it's unknown.") + case OS.Windows => ".dll" + case OS.Unknown => + throw Error("Cannot do lib compilation on this platform, it's unknown.") - def sendResourceToCache(name: String) = - println(name) + def sendResourceToCache(name: String): Unit = Files.createDirectories(appDataStore) val cacheLocation = appDataStore.resolve(s"$name.c") - if !Files.exists(appDataStore.resolve(s"$name.c")) then - Files.copy(getClass().getResourceAsStream(s"/native/$name.c"), cacheLocation) + println(cacheLocation) + if !Files.exists(appDataStore.resolve(s"$name.c")) then + val stream = getClass().getResourceAsStream(s"/native/$name.c") + if stream != null then Files.copy(stream, cacheLocation) + else throw Error(s"Could not find resource /native/$name.c") - def compileCachedResourceIfNeeded(name: String): Path = + def compileCachedResourceIfNeeded(name: String): Unit = val cacheLocation = appDataStore.resolve(s"$name$sharedLibSuffix") val headerLocation = appDataStore.resolve(s"$name.c") + println(headerLocation) + if !Files.exists(cacheLocation) then - val cmd = Seq("clang", "-shared", "-Os", "-o", cacheLocation.nn.toAbsolutePath().nn.toString(), headerLocation.nn.toAbsolutePath().nn.toString()) - if cmd.! != 0 then throw Error(s"failed to compile $headerLocation") - - cacheLocation.nn + val cmd = Seq( + "clang", + "-shared", + "-fvisibility=default", + "-Os", + "-o", + cacheLocation.nn.toAbsolutePath().nn.toString(), + headerLocation.nn.toAbsolutePath().nn.toString() + ) + if cmd.! != 0 then throw Error(s"failed to compile $headerLocation: ${cmd.mkString(" ")}") - def loadCachedLibrary(name: String) = - val cacheLocation = appDataStore.resolve(s"$name.$sharedLibSuffix") - System.loadLibrary(cacheLocation.nn.toAbsolutePath().nn.toString()) + def loadCachedLibrary(name: String) = + val cacheLocation = appDataStore.resolve(s"$name$sharedLibSuffix") + println(cacheLocation) + System.load(cacheLocation.nn.toAbsolutePath().nn.toString()) -} +end Tools diff --git a/core/test/resources/native/test.c b/core/test/resources/native/test.c new file mode 100644 index 00000000..6c483841 --- /dev/null +++ b/core/test/resources/native/test.c @@ -0,0 +1,13 @@ +#ifdef _WIN32 +# ifdef WIN_EXPORT +# define EXPORTED __declspec( dllexport ) +# else +# define EXPORTED __declspec( dllimport ) +# endif +#else +# define EXPORTED +#endif + +EXPORTED int identity_int(int i) { + return i; +} \ No newline at end of file diff --git a/core/test/src/fr/hammons/slinc/BindingsSpec.scala b/core/test/src/fr/hammons/slinc/BindingsSpec.scala index 5b1c3d9b..04a858e0 100644 --- a/core/test/src/fr/hammons/slinc/BindingsSpec.scala +++ b/core/test/src/fr/hammons/slinc/BindingsSpec.scala @@ -174,6 +174,7 @@ trait BindingsSpec(val slinc: Slinc) extends ScalaCheckSuite: test("int_identity") { assertEquals(Test.identity_int(5), 5) } + Test.identity_int(5) object BindingsSpec: case class div_t(quot: Int, rem: Int) diff --git a/j17/src/fr/hammons/slinc/Library17.scala b/j17/src/fr/hammons/slinc/Library17.scala index 0b1d6998..57c7792a 100644 --- a/j17/src/fr/hammons/slinc/Library17.scala +++ b/j17/src/fr/hammons/slinc/Library17.scala @@ -46,67 +46,28 @@ class Library17(layoutI: LayoutI, linker: CLinker) linker.downcallHandle(address.asInstanceOf[Addressable], md, fd).nn - private val standardLibLookup = new Lookup: - val lookupImpl = CLinker.systemLookup.nn - def lookup(name: String) = lookupImpl - .lookup(name) - .nn - .orElseThrow(() => - throw Error(s"Failed to load $name from the standard library") - ) - .nn - + class J17Lookup(s: SymbolLookup, libraryLocation: LibraryLocation) extends Lookup(libraryLocation): + def lookup(name: String): Object = s.lookup(name).nn.orElseThrow(() => throw this.lookupError(name)).nn + private val standardLibLookup = J17Lookup(CLinker.systemLookup().nn,LibraryLocation.Standardard) override def getStandardLibLookup: Lookup = Tools.hashCode() standardLibLookup - override def getLibraryPathLookup(libName: String): Lookup = - new Lookup: - System.loadLibrary(libName) - val lookupImpl = SymbolLookup.loaderLookup().nn - def lookup(symbolName: String) = lookupImpl - .lookup(symbolName) - .nn - .orElseThrow(() => - throw Error(s"Failed to load ${symbolName} from $libName") - ) - .nn + System.loadLibrary(libName) + J17Lookup(SymbolLookup.loaderLookup().nn, LibraryLocation.Path(libName)) + + + override def getResourceLibLookup(location: String): Lookup = + Tools.sendResourceToCache(location) + Tools.compileCachedResourceIfNeeded(location) + Tools.loadCachedLibrary(location) + + J17Lookup(SymbolLookup.loaderLookup().nn, LibraryLocation.Resource(location)) override def getLocalLookup(libPath: String): Lookup = - new Lookup: - System.load(libPath) + System.load(libPath) - val lookupImpl = SymbolLookup.loaderLookup().nn - def lookup(symbolName: String) = lookupImpl - .lookup(symbolName) - .nn - .orElseThrow(() => - throw Error(s"Failed to load $symbolName from $libPath") - ) - .nn + J17Lookup(SymbolLookup.loaderLookup().nn, LibraryLocation.Local(libPath)) - def getLookup(name: Option[String]): Lookup = - import scala.jdk.OptionConverters.* - name match - case Some(n) => - new Lookup: - if Files.exists(Paths.get(n)) then - System.load(Paths.get(n).nn.toRealPath().nn.toString()) - else System.loadLibrary(n) - val l = SymbolLookup.loaderLookup().nn - def lookup(name: String) = l - .lookup(name) - .nn - .toScala - .getOrElse(throw Error(s"Failed to load $name from $n")) - case None => - new Lookup: - val l = CLinker.systemLookup().nn - def lookup(name: String) = l - .lookup(name) - .nn - .toScala - .getOrElse( - throw Error(s"Failed to load $name from standard library") - ) +end Library17 \ No newline at end of file diff --git a/j17/test/resources/native/test.c b/j17/test/resources/native/test.c deleted file mode 100644 index 0583efdd..00000000 --- a/j17/test/resources/native/test.c +++ /dev/null @@ -1,3 +0,0 @@ -int identity_int(int i) { - return i; -} \ No newline at end of file diff --git a/j19/src/fr/hammons/slinc/Library19.scala b/j19/src/fr/hammons/slinc/Library19.scala index 18c7e04e..ccf62157 100644 --- a/j19/src/fr/hammons/slinc/Library19.scala +++ b/j19/src/fr/hammons/slinc/Library19.scala @@ -42,28 +42,24 @@ class Library19(layoutI: LayoutI, linker: Linker) linker.downcallHandle(address.asInstanceOf[Addressable], fd).nn - override def getLookup(name: Option[String]): Lookup = - import scala.jdk.OptionConverters.* + class J19Lookup(s: SymbolLookup, l: LibraryLocation) extends Lookup(l): + def lookup(name: String): Object = s.lookup(name).nn.orElseThrow(() => this.lookupError(name)).nn + override def getLibraryPathLookup(name: String): Lookup = + System.loadLibrary(name) - name match - case Some(n) => - new Lookup: - if Files.exists(Paths.get(n)) then - System.load(Paths.get(n).nn.toRealPath().nn.toString()) - else System.loadLibrary(n) - val l = SymbolLookup.loaderLookup().nn - def lookup(name: String) = - l.lookup(name) - .nn - .toScala - .getOrElse(throw Error(s"Lookup of $name in $n failed")) - case None => - new Lookup: - val l = linker.defaultLookup().nn - def lookup(name: String) = l - .lookup(name) - .nn - .toScala - .getOrElse( - throw Error(s"Lookup of $name in standard library failed") - ) + J19Lookup(SymbolLookup.loaderLookup().nn, LibraryLocation.Path(name)) + + override def getLocalLookup(name: String): Lookup = + System.load(name) + + J19Lookup(SymbolLookup.loaderLookup().nn, LibraryLocation.Local(name)) + + override def getResourceLibLookup(location: String): Lookup = + Tools.sendResourceToCache(location) + Tools.compileCachedResourceIfNeeded(location) + Tools.loadCachedLibrary(location) + + J19Lookup(SymbolLookup.loaderLookup().nn, LibraryLocation.Resource(location)) + + override def getStandardLibLookup: Lookup = J19Lookup(linker.defaultLookup().nn, LibraryLocation.Standardard) +end Library19 \ No newline at end of file