forked from jython/jython
-
Notifications
You must be signed in to change notification settings - Fork 0
/
build.gradle
916 lines (759 loc) · 31.8 KB
/
build.gradle
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
/*
* Gradle build for Jython. See also settings.gradle.
*
* This is an attempt to build a distributable JAR using Gradle that could be
* cited as a dependency by other Gradle or Maven projects, when they use the
* Jython interpreter from Java (under JSR-223 or directly).
*
* At present, the build is additional to the Ant build that remains the
* primary and reliable support for development, for test, and to build the
* Jython installers.
*
* The delivered jar should contain only Jython project material (Java classes
* and the Python library) while the many JARs Jython depends on will be cited
* in the accompanying metadata as dependencies.
*
* The Jython source structure does not follow the standard convention for
* Gradle. This script deals with that without changing it, but it uses a build
* directory (build2) entirely separate from Ant's, in which generated and
* compiled material is posted conformant with Gradle conventions. This means
* that the later tasks Gradle provides (test and jar) do not need so much
* customisation.
*/
plugins {
id 'java-library'
id 'antlr'
id 'maven-publish'
}
import java.text.SimpleDateFormat
// ---------------- Determine the version of Jython ----------------------------
/*
* This one string will be used to name the generated JAR and version-stamp the
* application. It should be all you have to edit to version a release built
* here. But of course you have to do it the hard way too (see build.xml) as
* long as Ant is also used.
*
* The convention here is that you specify the version of the *next* release. If
* there are files not checked in, extra files (not of types git ignores), or the
* changeset is not tagged correspondingly, the build will add "-SNAPSHOT" to
* the effective version.
*/
// Versions are specified in this grammar:
// <major> . <minor> ( . <micro> )? ( <release> <serial> )? ( - <word> )?
version = '2.7.3a1'
// Valid examples (please preserve in comments):
//version = '2.7.2a2'
//version = '2.7.2b2-DEV'
//version = '2.7.2b1'
//version = '2.7.2rc1'
//version = '2.7.2'
group = 'org.python'
// ---------------- Miscellaneous configuration --------------------------------
/*
* We support Java 8 onwards officially, up to v2.7.2, but retain the option of
* compiling for Java 7 (in v2.7.2) by maintaining compatibility in the code
* base and in the choice of JARs.
*/
sourceCompatibility = '1.7' // Make both 1.8 after 2.7.2 released
targetCompatibility = '1.8'
project.compileJava.options.debug = true
// Separate the Gradle build from that of Ant
buildDir = file('build2')
ext {
buildDate = new Date()
/*
* The directory structure supporting the build has separate locations for
* several intermediate stages.
*/
// Java source generated by ANTLR
antlrGenDir = "$buildDir/gensrc/org/python/antlr"
// Intermediate locations for compiled classes
unexposedDir = "$buildDir/unexposed"
exposedDir = "$buildDir/exposed"
// The standard library may safely be assembled in-place as a resource
pythonLibDir = "$buildDir/python/Lib/"
buildLibDir = "$buildDir/resources/main/Lib/"
buildTestLibDir = "$buildDir/resources/test/Lib/"
compiledLibDir = "$buildDir/resources/main/Lib/"
compiledTestLibDir = "$buildDir/resources/test/Lib/"
}
repositories {
// Jython is distributed through Maven Central. Get our dependencies there too.
mavenCentral()
}
sourceSets {
main { // Non-standard locations must be specified explicitly
antlr {
srcDirs = ['grammar']
exclude 'Base.g' // Not used (and produces errors)
}
java {
srcDirs = ['src', project.ext.antlrGenDir]
// Reference to proprietary libraries not supplied
exclude 'com/ziclix/python/sql/handler/InformixDataHandler.java'
exclude 'com/ziclix/python/sql/handler/OracleDataHandler.java'
}
resources {
// Resources in project root, but this invites an explosion.
// ... so claim no sources:
srcDirs = []
// and fix it in task processResources
}
}
test { // Non-standard locations must be specified explicitly
java {
srcDirs = ['tests/java']
// Reference to proprietary libraries not supplied
exclude 'com/ziclix/python/sql/**'
}
}
}
dependencies {
/*
* Must these correspond exactly with the external libraries (JARs)
* mentioned in the Ant build.xml? Or is some form of dynamic version
* better for downstream?
*
* Note that an application may specify a later version. Gradle will
* choose the latest required.
*/
// Using a version available from repo (not 'extlibs/servlet-api-2.5' as in build.xml)
implementation 'javax.servlet:javax.servlet-api:3.1.0'
/*
* These seem to be unnecessary while the proprietary database support is
* not bundled with Jython. Applications needing them can cite these or a
* version they prefer.
*/
//implementation 'mysql:mysql-connector-java:5.1.42'
//implementation 'org.postgresql:postgresql:42.1.1.jre7'
// pin to Antlr 3 until we upgrade parsing
antlr 'org.antlr:antlr:3.5.2'
implementation 'org.antlr:antlr-runtime:3.5.2'
implementation 'org.apache.commons:commons-compress:1.19'
implementation 'org.bouncycastle:bcpkix-jdk15on:1.62'
implementation 'org.bouncycastle:bcprov-jdk15on:1.62'
implementation 'org.ow2.asm:asm:7.1'
implementation 'org.ow2.asm:asm-commons:7.1'
implementation 'org.ow2.asm:asm-util:7.1'
// The Android Guava and "failureaccess" are necessary to support Java 7.
implementation 'com.google.guava:guava:28.0-android'
implementation 'com.google.guava:failureaccess:1.0.1'
// Swap for regular Guava at Java 8.
implementation 'com.ibm.icu:icu4j:59.1'
implementation 'com.carrotsearch:java-sizeof:0.0.5'
implementation 'com.github.jnr:jffi:1.2.20'
implementation 'com.github.jnr:jnr-netdb:1.1.6'
implementation 'com.github.jnr:jnr-ffi:2.1.10'
implementation 'com.github.jnr:jnr-posix:3.0.50'
implementation 'com.github.jnr:jnr-constants:0.9.12'
implementation 'jline:jline:2.14.5'
implementation 'io.netty:netty-buffer:4.1.45.Final'
implementation 'io.netty:netty-codec:4.1.45.Final'
implementation 'io.netty:netty-common:4.1.45.Final'
implementation 'io.netty:netty-handler:4.1.45.Final'
implementation 'io.netty:netty-resolver:4.1.45.Final'
implementation 'io.netty:netty-transport:4.1.45.Final'
// Used implicitly in the Ant build, must be explicit here
implementation 'org.apache.ant:ant:1.9.7'
// Pin to 4.10 until dependency on hamcrest classes resolved.
testImplementation 'junit:junit:4.10'
}
// ---------------- Resource Processing ----------------------------------------
/*
* Jython brings several files we could treat as resources, but they do not sit
* in the Gradle-conventional 'main/resources' directory, rather are in the
* project root or rub shoulders with the java source. Pick them individually.
*
* Several tasks defined below declare that processResources depends on them,
* with the objective that at the end of processResources all generated
* resources and the stdlib (but not the compiled stdlib) should be in place
* in $buildDir/resources/main.
*/
processResources {
from(file('.')) {
include 'LICENSE.txt'
}
from(file('src')) {
include 'META-INF/**'
include 'org/python/modules/ucnhash.dat'
}
}
// ---------------- ANTLR Task -------------------------------------------------
generateGrammarSource {
maxHeapSize = "512m"
outputDirectory = file(antlrGenDir)
}
// ---------------- compleJava Task --------------------------------------------
compileJava {
// Divert compiled classes to intermediate location pre-exposure.
destinationDir = file(unexposedDir)
}
// ---------------- Expose Task ------------------------------------------------
/*
* The exposer operates between the output of compileJava (unexposed directory)
* and a second intermediate location (exposed directory). These two the
* mergeExposed task will finally combine in the Gradle-standard classes
* directory used as input by the jar task.
*/
configurations {
expose.extendsFrom(implementation)
}
dependencies {
// The expose (Ant) task depends on classes compiled to here:
expose files(unexposedDir)
}
// A (Gradle) task to run the Ant task 'expose'.
task expose (group: 'Custom', dependsOn: compileJava) {
description = 'Expose Java types to Python using their annotations.'
// Allow Gradle to infer the need to regenreate the outputs
inputs.files(fileTree("${project.ext.unexposedDir}/org/python"))
outputs.dir(project.ext.exposedDir)
doLast {
/*
* Define an Ant task called 'expose' in the project's AntBuilder.
* We can't define it until ExposeTask has been compiled.
*/
ant.taskdef(
name: 'expose',
classname: 'org.python.expose.generate.ExposeTask',
classpath: configurations.expose.asPath
)
// Use the Gradle-conventional directory structure (not the legacy one).
ant.expose(
srcdir: file(project.ext.unexposedDir),
destdir: mkdir(file(project.ext.exposedDir)),
includesfile: file('CoreExposed.includes')
)
}
}
// Task to merge the exposed and unexposed classes
task mergeExposed(group: 'Custom', type:Copy, dependsOn: expose) {
description = 'Copy exposed Java types to classes.'
// Exposed version will take precedence
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
from file(exposedDir)
from file(unexposedDir)
into sourceSets.main.output.classesDirs.singleFile
}
// Attach to the classes task the placing of all compiled and exposed classes.
classes.dependsOn(mergeExposed)
// ---------------- Version-related file generation ----------------------------
/*
* Write the information that org.python.Version reads from
* org/python/version.properties in the class file structure. The inputs to
* this are: information from Git (git command required); project.version;
* and project.ext.buildDate. The task works quite hard to decode
* project.version, which must have the correct form, to deduce whether you
* really intend this to be a release. If anything comes after the release
* number, typically it's a '+' sign, the version becomes a snapshot.
*/
task generateVersionInfo(
type: WriteProperties,
description: 'Write the version information as properties') {
outputFile = file("${processResources.destinationDir}/org/python/version.properties")
comment = ' Jython version information (from build.gradle)'
// Create the properties when the task runs. But do it before the write!
doFirst {
/*
* Query Git for version and tagging. The git commands are exactly
* those used by CPython to create constants configure.ac, but do not
* correspond very well to their names.
*/
// Not much like a branch. Used only if git_tag is blank.
def branch = 'git name-rev --name-only HEAD'.execute().text.split('\n', 2)[0]
property('jython.build.git_branch', branch)
println " jython.build.git_branch = ${branch}"
// When tagged, the result is "tags/vX.Y.Zrc9", or whatever.
// When not, tagged it is the (short) revision number.
def tag = 'git describe --all --always --dirty'.execute().text.split('\n', 2)[0]
property('jython.build.git_tag', tag)
println " jython.build.git_tag = ${tag}"
// Revision number (short = 7 hex digits)
def ident = 'git rev-parse --short HEAD'.execute().text.split('\n', 2)[0]
property('jython.build.git_version', ident)
println " jython.build.git_version = ${ident}"
/*
* Decompose the version string into elements for Jython to access as
* properties. (The Ant build.xml requires them to be set in parts, but
* we can work it out from project.version.)
*/
// <major>.<minor>(.<micro>)(<release><serial>)?(+|-<snapshot>)?
def versionRegex = /(\d+)\.(\d+)(\.(\d+))?((a|b|rc)(\d+))?(\+|-(\w+))?/
def versionMatcher = project.version =~ versionRegex
if (versionMatcher.count != 1) {
throw new IllegalArgumentException(
"Cannot parse project version string '${project.version}'")
}
// In principle it could match more than once: take the leftmost
def versionResult = versionMatcher[0]
// <major>.<minor> means <major>.<minor>.0
String major = versionResult[1]
String minor = versionResult[2]
String micro = versionResult[3] ? versionResult[4] : '0'
// Convert the optional <release><serial> to numbers
int level = 0, serial = 0
if (versionResult[5]) {
// This is some kind of pre-final release (unless snapshot)
serial = versionResult[7] as int
switch (versionResult[6]) {
case 'a': level = 0xa; break // ALPHA release
case 'b': level = 0xb; break // BETA release
case 'rc': level = 0xc; break // release candidate
}
} else {
// Not marked as a/b/rc so ostensibly a final release.
level = 0xf
}
// Convert optional +|-<word> to -DEV or -SNAPSHOT suffix or empty string
String snapshotSuffix = versionResult[8];
if (snapshotSuffix == '+') {
snapshotSuffix = "-SNAPSHOT"
}
/*
* Work out if it looks like a release, or adjust project.version. This logic prevents us
* releasing from a polluted repo (similar to logic in the Ant build.xml).
*/
def L = [0:'', 10:'a', 11:'b', 12:'rc', 15:'']
String release = "$major.$minor.$micro${L[level]}${serial?:''}"
if (snapshotSuffix == null) {
// The version is named correctly for a release. Make safety checks on the repo.
String expectTag = "tags/v$release"
String message = null;
if (tag.endsWith('-dirty')) {
message = 'Version-controlled files have been edited since the last commit'
} else if (tag != expectTag) {
message = "Change set $ident is not tagged $expectTag."
} else {
// Query Git for status: non-empty if uncontrolled (unignored) files.
String gitStatus = 'git status --porcelain'.execute().text
if (gitStatus.trim().length() > 0) {
message = 'Workspace contains uncontrolled files'
}
}
// If a message was set for any reason, fall back to a snapshot.
if (message == null) {
// Repository state is good for a full build.
snapshotSuffix = ''
} else {
// Some reason found not to build the release.
println "$message - build is a snapshot."
snapshotSuffix = '-SNAPSHOT'
}
}
// Rebuild the version with the snapshot suffix, even if not given originally.
project.version = release + snapshotSuffix
println "This build is for v${project.version}."
property('jython.version', project.version)
property('jython.major_version', major)
property('jython.minor_version', minor)
property('jython.micro_version', micro)
property('jython.release_level', level)
property('jython.release_serial', serial)
/*
* Time-stamp the build. In the time part, the ':' gets escaped to
* '\:', consistent with Properties.store(), unlike the Ant build.
*/
property('jython.build.time',
(new SimpleDateFormat('HH:mm:ss'))
.format(project.ext.buildDate))
property('jython.build.date',
(new SimpleDateFormat('MMM d yyyy'))
.format(project.ext.buildDate))
}
}
// Attach this task to processResources
processResources.dependsOn(generateVersionInfo)
// ---------------- Copy Python Library ----------------------------------------
/*
* The default behaviour of the Java plug-in is to make a JAR of the classes in
* the "main" source set. We need a more complex assembly that provides users
* with exposed classes instead of their plain counterparts, and also various
* configuration files and the Python library.
*
* These copies include the tests, so we can test things :), but a subsequent
* JarTask of the build should exclude them as necessary. (Not yet implemented.)
*/
ext {
libPython = 'lib-python/2.7'
libJython = 'Lib'
libTestSpecs = [
'distutils/tests/',
'email/test/',
'json/tests/',
'lib2to3/tests/',
'unittest/test/',
'test/'
]
}
/*
* Copy the Python standard library. We take this from a distribution of
* CPython, but take only the files specified in CPythonLib.includes.
* The Jython version of the standard library will be copied to the same place.
* Files from the Jython library having the same name (relative path) as one
* in CPythonLib.includes thereby take precedence.
*/
task mergePythonLib(
type: Copy,
description: 'Merge lib-python and Jython Lib') {
// There might be a way using a collection of File rather than actual copy.
into pythonLibDir
// Copy Jython Lib, with precedence over CPython files of the same name
duplicatesStrategy = DuplicatesStrategy.INCLUDE
from libJython
exclude '**/*.class'
// Allow Gradle to infer the need to regenerate the outputs
inputs.dir libJython
inputs.dir libPython
inputs.file file('CPythonLib.includes')
doFirst {
// Select the CPython stdlib files by making a list.
def cPythonLibIncludes = []
// Read list from CPythonLib.includes, stripping comments and spaces.
file('CPythonLib.includes').eachLine { line ->
def trimmed = line.split('#', 2)[0].trim()
if (trimmed.length() > 0) {
cPythonLibIncludes << trimmed
}
}
// Copy the subset as specified by the list
project.copy {
into pythonLibDir
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
from libPython
include cPythonLibIncludes
exclude '**/*.pyc', '**/*.pyd'
}
}
}
/*
* Copy from the merge location into the main resources, excluding material
* only needed for tests.
*/
task copyLib(
type: Copy,
dependsOn: mergePythonLib,
description: 'Copy merged Python library (main excluding tests)') {
into buildLibDir
from pythonLibDir
exclude '**/*.pyc', '**/*.pyd', '**/*.class'
// Exclude tests and test material
exclude libTestSpecs
}
// Attach this task to processResources
processResources.dependsOn(copyLib)
/*
* Copy from the merge location into the test resopurces, including only
* that extra material needed for tests.
*/
task copyTestLib(
type: Copy,
dependsOn: mergePythonLib,
description: 'Copy merged Python library (tests only)') {
into buildTestLibDir
from pythonLibDir
exclude '**/*.pyd', '**/*.class' // test material includes .pyc files
// Include only tests and test material
include libTestSpecs
}
// Attach this task to processResources
processTestResources.dependsOn(copyTestLib)
// ---------------- Jython-Compile Python --------------------------------------
/*
* Compile the Python modules to .class files for the JAR. Whereas Jython runs
* happily with a concrete Lib folder, creating and caching the .class files,
* when Jython is supplied as a JAR, we prefer to compile the class files once
* in advance.
*/
configurations {
pycompile.extendsFrom(implementation)
}
dependencies {
// Jython as built so far should be on the path of the jycompile (Ant) task
pycompile files("$buildDir/classes/java/main")
pycompile files("$buildDir/resources/main")
}
// A (Gradle) task to run the Ant task 'jycompile' (not pycompile).
task pycompile(
group: 'Custom',
description: 'Compile the Python modules to .class files for the JAR') {
// Compiler depends on rest of Jython being fully assembled in 'classes'
dependsOn classes
// Note that classes depends on processResources (Java plug-in).
// Allow Gradle to infer the need to regenerate the outputs
inputs.dir project.ext.buildLibDir
outputs.dir project.ext.compiledLibDir
doFirst {
/*
* Define an Ant task called 'jycompile' in the project's AntBuilder.
* We can't define it until JythoncAntTask has been compiled, so this
* must happen during the execution of the task (early).
*/
ant.taskdef(
name: 'jycompile',
classname: 'org.python.util.JycompileAntTask',
classpath: configurations.pycompile.asPath
)
}
doLast {
/*
* Now use the 'jycompile' Ant task to compile the Python source we
* supply to users. The exclusions have been copied from build.xml,
* and also this comment:
<!-- One might think that the exclusion of lib2to3/tests/** is
recursive, but one would be wrong ;) It's actually only
two levels, so for now the workaround is also to include
data/myfixes/**
This exclusion for lib2to3/tests/ in general is necessary
because data/infinite_recursion.py is not compileable by
Jython - it's too large and will exceed Java method
limits for the top level script; nor is
data/py3_test_grammar.py - it's Python 3. Meanwhile
refactor.get_all_fix_names depends on having *.py, not
exclusively $py.class, files available in
lib2to3/tests/data/myfixes/**. -->
*/
def exclusions = ['test/**', 'lib2to3/tests/**',
'lib2to3/tests/data/myfixes/**']
ant.jycompile(
srcdir: project.ext.buildLibDir,
destdir: project.ext.compiledLibDir,
excludes: exclusions.join(',') // Yes, it's that way round :o
)
}
}
// ---------------- Building the JARs ------------------------------------------
/*
* The default behaviour of the Java plug-in is to make a JAR of the classes in
* the "main" source set and its resources. Having carefully substituted/added
* exposed classes in the assembled classes directory, and having prepared the
* (compiled) stdlib as a resource, this is close to what we need, with a few
* adjustments as noted.
*/
jar {
// Ensure that compiled stdlib is part of the resources to JAR.
dependsOn pycompile
// It is important for import that X$py.class be newer than X.py
preserveFileTimestamps = true
// We don't JAR the expose tool itself
exclude 'org/python/expose/generate/**'
// Build a custom manifest
manifest {
// These attribute values are based on inspecting the ant build
attributes ([
'Main-Class': 'org.python.util.jython',
'Built-By': 'build.gradle',
])
attributes( [ // Build-Info section
'version': project.version,
'build-compiler': 'modern',
'jdk-target-version': project.targetCompatibility,
'debug': compileJava.options.debug,
'informix': false,
'oracle': false
], 'Build-Info' )
}
}
/*
* This is a task complementary to the jar task, taking just the test material.
* This is not published via the main repositories because it counts as a
* distinct artefact with its own POM.
*/
// XXX Consider instead a multi-project build with one artefact per sub-project.
task testJar(type: Jar) {
classifier = 'tests'
from sourceSets.test.output
// We don't JAR the expose tool, so we don't JAR the tests
exclude 'org/python/expose/generate/**'
// Build a custom manifest
manifest {
// These attribute values are based on inspecting the ant build
attributes ([
//'Main-Class': 'org.python.util.jython',
'Built-By': 'build.gradle',
])
attributes( [ // Build-Info section
'version': project.version,
'build-compiler': 'modern',
'jdk-target-version': project.targetCompatibility,
'debug': compileTestJava.options.debug
], 'Build-Info' )
}
}
// ---------------- Documentation ----------------------------------------------
/*
* The JavaDoc, anyway.
*/
javadoc {
options.encoding = 'UTF-8'
source = fileTree(dir: 'src', include: '**/*.java')
}
// ---------------- Publication ------------------------------------------------
/*
* Post the JAR we built to a public repository. We provide secondary -source
* and -javadoc JARs too (supporting 'main').
*
* How do we test the artifact actually published is correct? The 'test' task
* tests Jython laid out in the build directory, not the JAR we propose to
* distribute.
*
* Maybe have a second JAR that contains the additional material necessary to
* run integration tests (regression tests and others).
*/
task sourcesJar(type: Jar, dependsOn: classes) {
classifier = 'sources'
from sourceSets.main.allJava
}
task javadocJar(type: Jar, dependsOn: javadoc) {
classifier = 'javadoc'
from javadoc.destinationDir
}
publishing {
publications {
// The production JAR we expect to be cited as a dependency by users
main(MavenPublication) {
from components.java
// Also provide the source.
artifact sourcesJar
// Also provide the docs. (Some javadoc errors currently.)
artifact javadocJar
pom {
// Same description as in ~/maven/pom-template
name = 'Jython'
description =
'Jython is an implementation of the high-level, dynamic, object-oriented\n' +
'language Python written in 100% Pure Java, and seamlessly integrated with\n' +
'the Java platform. It thus allows you to run Python on any Java platform.'
url = 'https://www.jython.org/'
// We use the PSF 2.0, but only most recently, and actually a bundle.
licenses {
license {
name = 'Jython Software License'
// Not actually the license URL, but linked from here.
url = 'https://www.jython.org/'
distribution = 'repo'
}
}
// Point to Git repositories hosted on GitHub.
scm {
connection = 'scm:git:https://github.com/jython/jython.git'
developerConnection = 'scm:git:ssh://[email protected]:jython/jython.git'
url = 'https://github.com/jython/jython'
}
// Could list us all, but why not just the list for now?
developers {
developer {
id = 'jython'
name = 'Jython Developers'
email = '[email protected]'
}
}
}
}
}
repositories {
// Staging area where ant -f maven/build.xml will look.
maven {
name = 'stagingRepo'
url = "file://${buildDir}/stagingRepo"
}
}
}
// Ensure version computation/branding precedes any publication we use.
publish.dependsOn(generateVersionInfo)
/* FIXME: Depending on publishMainPublicationToMavenLocal does not work,
because it does not exist when evaluating this line. Is this the deferred
configuration removed in Gradle 5.0? Failsd on POM version mismatch if main
publish task not run before publishMainPublicationToMavenLocal.
*/
//publishMainPublicationToMavenLocal.dependsOn(generateVersionInfo)
// ---------------- Java unit tests --------------------------------------------
ext {
//distDir = relativePath("$buildDir/assembly")
testSourceDir = relativePath('tests/java')
}
dependencies {
// Put the exposed classes on the path of the test tasks
testImplementation files(expose)
}
// Ensure exposed classes are ahead of standard path
sourceSets.test {
compileClasspath = files(expose.outputs) + compileClasspath
runtimeClasspath = files(expose.outputs) + runtimeClasspath
// println "runtimeClasspath = ${runtimeClasspath.asPath}"
}
compileTestJava {
dependsOn expose
options.debug = project.compileJava.options.debug
}
test {
dependsOn copyLib
// Stop on first test failure
failFast = true
// Properties as defined in Ant target javatest-basepath
// XXX Not sure of all that python.home is used for in tests.
systemProperty 'python.home', file(copyLib.destinationDir).parent
systemProperty 'python.test.source.dir', project.ext.testSourceDir
// Place cache outside the targets for jar task
systemProperty 'python.cachedir', "${project.buildDir}/cachedir"
// Logging level: default is message=INFO
//systemProperty 'python.verbose', 'CONFIG'
include '**/*Test*'
// Exclude based on Ant target javatest-basepath
exclude '**/InterpTestCase'
exclude '**/jythonTest*' // Must run interactively
exclude 'org/python/antlr/**'
exclude 'org/python/tests/imp/**' // See build.xml:importest
// Some additional exclusions or else the task fails
// FIXME: leaves stdin/out/err as PyFileWriter that has no fileno()
// causing _ioTest to fail.
exclude '**/jsr223/*'
// FIXME: Tests that hard-code directory paths (use a symbol):
exclude 'org/python/compiler/custom_proxymaker/**'
exclude 'org/python/compiler/JavaMakerSmokeTest.class'
// FIXME: Failing test finds project root from test class location
exclude 'org/python/core/PySystemState_registry_Test.class'
// FIXME: Fails as sys._jy_console not set when run under Gradle
exclude 'org/python/util/InterpreterTest.class'
doFirst {
println "systemProperties = $systemProperties"
}
}
// ---------------- Miscellaneous fettling of the prepare phase ----------------
// Source is globally UTF-8 (well, nearly).
tasks.withType(JavaCompile) {
options.encoding = "UTF-8"
}
// ---------------- Support for debugging --------------------------------------
afterEvaluate { project ->
//dumpCP()
//dumpSS()
}
void dumpCP() {
println('\nconfigurations.testCompile:')
configurations.testCompile.each { println it }
println('\nconfigurations.testRuntime:')
configurations.testRuntime.each { println it }
println('\nconfigurations.expose:')
configurations.expose.each { println it }
println('\nconfigurations.pycompile:')
configurations.pycompile.each { println it }
}
void dumpSS() {
println '*** source sets ***'
for (ss in sourceSets) {
String name = ss.name
println ss
println " ${name}.compileConfigurationName = ${ss.compileConfigurationName}"
println " ${name}.implementationConfigurationName = ${ss.implementationConfigurationName}"
println " ${name}.runtimeConfigurationName = ${ss.runtimeConfigurationName}"
println " ${name}.java.srcDirs = ${ss.java.srcDirs}"
println " ${name}.antlr.srcDirs = ${ss.antlr.srcDirs}"
println " ${name}.resources.srcDirs = ${ss.resources.srcDirs}"
println " ${name}.output.dirs = ${ss.output.dirs.files}"
println " ${name}.output.classesDirs = ${ss.output.classesDirs.files}"
println " ${name}.output.resourcesDir = ${ss.output.resourcesDir}"
println " ${name}.classesTaskName = ${ss.classesTaskName}"
println " ${name}.compileJavaTaskName = ${ss.compileJavaTaskName}"
println " ${name}.jarTaskName = ${ss.jarTaskName}"
}
}