Skip to content

Commit 8b064c7

Browse files
Initial commit
0 parents  commit 8b064c7

File tree

13 files changed

+586
-0
lines changed

13 files changed

+586
-0
lines changed

.github/dependabot.yml

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
version: 2
2+
updates:
3+
4+
- package-ecosystem: "github-actions"
5+
directory: "/"
6+
schedule:
7+
interval: "weekly"

.github/scripts/ci.sh

+32
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
#!/usr/bin/env bash
2+
set -exo pipefail
3+
4+
TEST_VERSION="0.1.0-test"
5+
6+
echo "Running tests"
7+
sbt +test
8+
9+
IS_UNIX="false"
10+
case "$(uname -s)" in
11+
Linux*) IS_UNIX="true";;
12+
Darwin*) IS_UNIX="true";;
13+
*)
14+
esac
15+
16+
if [ "$IS_UNIX" == "true" ]; then
17+
echo "Publishing locally"
18+
sbt \
19+
'set version in ThisBuild := "'"$TEST_VERSION"'"' \
20+
publishLocal
21+
echo "Running JDK 11 tests…"
22+
# test that things work from JDK 11
23+
# not actually building things from it, running into weird proguard issues…
24+
25+
TEST_JDK="adopt:1.11.0-7"
26+
eval "$(cs java --jvm "$TEST_JDK" --env)"
27+
28+
java -Xmx32m -version
29+
30+
export TEST_VERSION
31+
sbt test
32+
fi

.github/scripts/gpg-setup.sh

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
#!/usr/bin/env sh
2+
3+
# from https://github.com/coursier/apps/blob/f1d2bf568bf466a98569a85c3f23c5f3a8eb5360/.github/scripts/gpg-setup.sh
4+
5+
echo $PGP_SECRET | base64 --decode | gpg --import --no-tty --batch --yes
6+
7+
echo "allow-loopback-pinentry" >>~/.gnupg/gpg-agent.conf
8+
echo "pinentry-mode loopback" >>~/.gnupg/gpg.conf
9+
10+
gpg-connect-agent reloadagent /bye

.github/workflows/ci.yml

+55
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
name: CI
2+
on:
3+
push:
4+
branches:
5+
- main
6+
tags:
7+
- "v*"
8+
pull_request:
9+
10+
jobs:
11+
test:
12+
runs-on: ${{ matrix.OS }}
13+
name: Test ${{ matrix.OS }}
14+
strategy:
15+
fail-fast: false
16+
matrix:
17+
OS: ["ubuntu-latest", "macos-latest", "windows-latest"]
18+
steps:
19+
- uses: actions/checkout@v3
20+
with:
21+
fetch-depth: 0
22+
submodules: true
23+
- uses: coursier/cache-action@v6
24+
- uses: coursier/[email protected]
25+
with:
26+
jvm: 8
27+
apps: sbt
28+
- name: Test
29+
run: ./.github/scripts/ci.sh
30+
shell: bash
31+
32+
publish:
33+
needs: test
34+
if: github.event_name == 'push'
35+
runs-on: ubuntu-latest
36+
steps:
37+
- uses: actions/checkout@v3
38+
with:
39+
fetch-depth: 0
40+
submodules: true
41+
- uses: coursier/cache-action@v6
42+
- uses: coursier/[email protected]
43+
with:
44+
jvm: 8
45+
- run: .github/scripts/gpg-setup.sh
46+
shell: bash
47+
env:
48+
PGP_SECRET: ${{ secrets.PUBLISH_SECRET_KEY }}
49+
- name: Release
50+
run: sbtn ci-release
51+
env:
52+
PGP_SECRET: ${{ secrets.PUBLISH_SECRET_KEY }}
53+
PGP_PASSPHRASE: ${{ secrets.PUBLISH_SECRET_KEY_PASSWORD }}
54+
SONATYPE_USERNAME: ${{ secrets.PUBLISH_USER }}
55+
SONATYPE_PASSWORD: ${{ secrets.PUBLISH_PASSWORD }}

.gitignore

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
target/
2+
.bsp/

build.sbt

+227
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,227 @@
1+
import scala.xml.{Node => XmlNode, _}
2+
import scala.xml.transform.{RewriteRule, RuleTransformer}
3+
4+
inThisBuild(List(
5+
organization := "io.github.alexarchambault.python",
6+
homepage := Some(url("https://github.com/alexarchambault/python-native-libs-interface")),
7+
licenses := List("Apache-2.0" -> url("http://www.apache.org/licenses/LICENSE-2.0")),
8+
developers := List(
9+
Developer(
10+
"alexarchambault",
11+
"Alexandre Archambault",
12+
13+
url("https://github.com/alexarchambault")
14+
)
15+
),
16+
sonatypeCredentialHost := "s01.oss.sonatype.org",
17+
sonatypeRepository := "https://s01.oss.sonatype.org/service/local",
18+
sonatypeProfileName := "io.github.alexarchambault"
19+
))
20+
21+
lazy val finalPackageBin = taskKey[File]("")
22+
23+
def updateIvyXml(file: File): Unit = {
24+
val content = scala.xml.XML.loadFile(file)
25+
val updatedContent = content.copy(child = content.child.map {
26+
case elem: Elem if elem.label == "dependencies" =>
27+
elem.copy(child = elem.child.map {
28+
case elem: Elem if elem.label == "dependency" =>
29+
(elem.attributes.get("org"), elem.attributes.get("name")) match {
30+
case (Some(Seq(orgNode)), Some(Seq(nameNode))) =>
31+
val org = orgNode.text
32+
val name = nameNode.text
33+
Comment(
34+
s""" shaded dependency $org:$name
35+
| $elem
36+
|""".stripMargin
37+
)
38+
case _ => elem
39+
}
40+
case n => n
41+
})
42+
case n => n
43+
})
44+
val printer = new scala.xml.PrettyPrinter(Int.MaxValue, 2)
45+
val updatedFileContent = """<?xml version="1.0" encoding="UTF-8"?>""" + '\n' +
46+
"<!-- hello -->\n" +
47+
printer.format(updatedContent)
48+
java.nio.file.Files.write(file.toPath, updatedFileContent.getBytes("UTF-8"))
49+
}
50+
51+
lazy val reallyUpdateIvyXml = Def.task {
52+
val baseFile = deliverLocal.value
53+
val log = streams.value.log
54+
val resolverName = publishLocalConfiguration.value.resolverName.getOrElse(???)
55+
ivyModule.value.withModule(log) {
56+
case (ivy, md, _) =>
57+
val resolver = ivy.getSettings.getResolver(resolverName)
58+
val artifact = new org.apache.ivy.core.module.descriptor.MDArtifact(md, "ivy", "ivy", "xml", true)
59+
log.info(s"Writing ivy.xml with shading at $baseFile")
60+
resolver.publish(artifact, baseFile, true)
61+
}
62+
}
63+
64+
lazy val interface = project
65+
.enablePlugins(SbtProguard)
66+
.settings(
67+
moduleName := {
68+
val former = moduleName.value
69+
val sv = scalaVersion.value
70+
val sbv = sv.split('.').take(2).mkString(".")
71+
if (sv == Settings.scala213)
72+
former
73+
else
74+
former + "-scala-" + sbv + "-shaded"
75+
},
76+
finalPackageBin := {
77+
import com.eed3si9n.jarjar._
78+
import com.eed3si9n.jarjar.util.StandaloneJarProcessor
79+
80+
val orig = (Proguard / proguard).value.head
81+
val origLastModified = orig.lastModified()
82+
val dest = orig.getParentFile / s"${orig.getName.stripSuffix(".jar")}-with-renaming-test.jar"
83+
if (!dest.exists() || dest.lastModified() < origLastModified) {
84+
val tmpDest = orig.getParentFile / s"${orig.getName.stripSuffix(".jar")}-with-renaming-test-0.jar"
85+
86+
def rename(from: String, to: String): Rule = {
87+
val rule = new Rule
88+
rule.setPattern(from)
89+
rule.setResult(to)
90+
rule
91+
}
92+
93+
val rules = Seq(
94+
rename("scala.**", "io.github.alexarchambault.pythonnativelibs.shaded.scala.@1"),
95+
rename("ai.kien.python.**", "io.github.alexarchambault.pythonnativelibs.shaded.python.@1"),
96+
// rename("org.fusesource.**", "io.github.alexarchambault.pythonnativelibs.shaded.org.fusesource.@1"),
97+
// rename("io.github.alexarchambault.windowsansi.**", "io.github.alexarchambault.pythonnativelibs.shaded.windowsansi.@1"),
98+
// rename("concurrentrefhashmap.**", "io.github.alexarchambault.pythonnativelibs.shaded.concurrentrefhashmap.@1"),
99+
// rename("org.apache.commons.compress.**", "io.github.alexarchambault.pythonnativelibs.shaded.commonscompress.@1"),
100+
// rename("org.apache.commons.io.input.**", "io.github.alexarchambault.pythonnativelibs.shaded.commonsio.@1"),
101+
// rename("org.codehaus.plexus.**", "io.github.alexarchambault.pythonnativelibs.shaded.plexus.@1"),
102+
// rename("org.tukaani.xz.**", "io.github.alexarchambault.pythonnativelibs.shaded.xz.@1"),
103+
// rename("org.iq80.snappy.**", "io.github.alexarchambault.pythonnativelibs.shaded.snappy.@1"),
104+
// rename("com.github.plokhotnyuk.jsoniter_scala.core.**", "io.github.alexarchambault.pythonnativelibs.shaded.jsoniter.@1")
105+
)
106+
107+
val processor = new com.eed3si9n.jarjar.JJProcessor(
108+
rules,
109+
verbose = false,
110+
skipManifest = true,
111+
misplacedClassStrategy = "fatal"
112+
)
113+
StandaloneJarProcessor.run(orig, tmpDest, processor)
114+
115+
val toBeRemoved = Set(
116+
"LICENSE",
117+
"NOTICE",
118+
"README",
119+
"scala-collection-compat.properties"
120+
)
121+
val directoriesToBeRemoved = Seq(
122+
"licenses/"
123+
)
124+
assert(directoriesToBeRemoved.forall(_.endsWith("/")))
125+
ZipUtil.removeFromZip(
126+
tmpDest,
127+
dest,
128+
name =>
129+
toBeRemoved(name) || directoriesToBeRemoved.exists(dir =>
130+
name.startsWith(dir)
131+
)
132+
)
133+
tmpDest.delete()
134+
}
135+
Check.onlyNamespace("io/github/alexarchambault/pythonnativelibs", dest)
136+
dest
137+
},
138+
addArtifact(Compile / packageBin / artifact, finalPackageBin),
139+
Proguard / proguardVersion := "7.2.2",
140+
Proguard / proguardOptions ++= {
141+
val baseOptions = Seq(
142+
"-dontnote",
143+
"-dontwarn",
144+
"-dontobfuscate",
145+
"-dontoptimize",
146+
"-keep class io.github.alexarchambault.pythonnativelibs.** {\n public protected *;\n}"
147+
)
148+
149+
val isJava9OrMore = sys.props.get("java.version").exists(!_.startsWith("1."))
150+
val maybeJava9Options =
151+
if (isJava9OrMore) {
152+
val javaHome = sys.props.getOrElse("java.home", ???)
153+
Seq(s"-libraryjars $javaHome/jmods/java.base.jmod")
154+
}
155+
else
156+
Nil
157+
158+
baseOptions ++ maybeJava9Options
159+
},
160+
Proguard / proguard / javaOptions := Seq("-Xmx3172M"),
161+
162+
// Adding the interface JAR rather than its classes directory.
163+
// The former contains META-INF stuff in particular.
164+
Proguard / proguardInputs := (Proguard / proguardInputs).value.filter(f => !f.isDirectory || f.getName != "classes"),
165+
Proguard / proguardInputs += (Compile / packageBin).value,
166+
167+
Proguard / proguardBinaryDeps := Settings.getAllBinaryDeps.value,
168+
Proguard / proguardBinaryDeps ++= Settings.rtJarOpt.toSeq, // seems needed with sbt 1.4.0
169+
170+
Proguard / proguardInputFilter := { file =>
171+
file.name match {
172+
case n if n.startsWith("interface") => None // keep META-INF from main JAR
173+
case n if n.startsWith("scala-xml") => Some("!META-INF/**,!scala-xml.properties")
174+
case n if n.startsWith("scala-library") => Some("!META-INF/**,!library.properties,!rootdoc.txt")
175+
case _ => Some("!META-INF/**")
176+
}
177+
},
178+
179+
publishLocal := {
180+
reallyUpdateIvyXml.dependsOn(publishLocal).value
181+
},
182+
183+
deliverLocal := {
184+
val file = deliverLocal.value
185+
updateIvyXml(file)
186+
file
187+
},
188+
189+
// inspired by https://github.com/olafurpg/coursier-small/blob/408528d10cea1694c536f55ba1b023e55af3e0b2/build.sbt#L44-L56
190+
pomPostProcess := { node =>
191+
new RuleTransformer(new RewriteRule {
192+
override def transform(node: XmlNode) = node match {
193+
case _: Elem if node.label == "dependency" =>
194+
val org = node.child.find(_.label == "groupId").fold("")(_.text.trim)
195+
val name = node.child.find(_.label == "artifactId").fold("")(_.text.trim)
196+
val ver = node.child.find(_.label == "version").fold("")(_.text.trim)
197+
Comment(s"shaded dependency $org:$name:$ver")
198+
case _ => node
199+
}
200+
}).transform(node).head
201+
},
202+
203+
Settings.shared,
204+
libraryDependencies ++= Seq(
205+
"ai.kien" %% "python-native-libs" % "0.2.4"
206+
),
207+
208+
libraryDependencies += "com.lihaoyi" %% "utest" % "0.8.0" % Test,
209+
testFrameworks += new TestFramework("utest.runner.Framework"),
210+
211+
// clearing scalaModuleInfo in ivyModule, so that evicted doesn't
212+
// check scala versions
213+
ivyModule := {
214+
val is = ivySbt.value
215+
val config = moduleSettings.value match {
216+
case config0: ModuleDescriptorConfiguration =>
217+
config0.withScalaModuleInfo(None)
218+
case other => other
219+
}
220+
new is.Module(config)
221+
},
222+
autoScalaLibrary := false,
223+
crossVersion := CrossVersion.disabled
224+
)
225+
226+
publish / skip := true
227+
Settings.shared
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
package io.github.alexarchambault.pythonnativelibs;
2+
3+
import io.github.alexarchambault.pythonnativelibs.internal.PythonNativeLibsHelper;
4+
5+
import java.util.Map;
6+
7+
public final class PythonNativeLibs {
8+
9+
public static String[] ldflags() {
10+
return PythonNativeLibsHelper.ldflags();
11+
}
12+
13+
public static String executable() {
14+
return PythonNativeLibsHelper.executable();
15+
}
16+
17+
public static String[] ldflagsNix() {
18+
return PythonNativeLibsHelper.ldflagsNix();
19+
}
20+
21+
public static String[] ldflagsWin() {
22+
return PythonNativeLibsHelper.ldflagsWin();
23+
}
24+
25+
public static String nativeLibrary() {
26+
return PythonNativeLibsHelper.nativeLibrary();
27+
}
28+
29+
public static String[] nativeLibraryPaths() {
30+
return PythonNativeLibsHelper.nativeLibraryPaths();
31+
}
32+
33+
public static Map<String, String> scalapyProperties() {
34+
return PythonNativeLibsHelper.scalapyProperties();
35+
}
36+
37+
}

0 commit comments

Comments
 (0)