Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file removed LewisOmniscientDebugger-1.5.jar
Binary file not shown.
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,11 @@ compile group: 'com.neuronrobotics', name: 'LewisOmniscientDebugger', version: '
# Usage
If you normally run your program on UNIX like this:

`` % java com.lambda.tests.TestMyArrayList ``
`` % java your.package.Main ``

You can run the debugger like this:

`` % java -cp LewisOmniscientDebugger-1.5.jar:$CLASSPATH com.lambda.Debugger.Debugger com.lambda.tests.TestMyArrayList ``
`` % java -cp build/libs/LewisOmniscientDebugger.jar:$CLASSPATH com.lambda.Debugger.Debugger your.package.Main ``

There are alias files and .BAT files that allow you to type this:

Expand Down
102 changes: 85 additions & 17 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -24,20 +24,21 @@ repositories {
}

dependencies {
implementation 'org.apache.bcel:bcel:6.2'
implementation 'org.apache.bcel:bcel:6.12.0'
implementation 'org.apache.commons:commons-lang3:3.20.0'
implementation 'commons-io:commons-io:2.21.0'
implementation 'org.ow2.asm:asm:9.7.1'
testImplementation 'junit:junit:4.13.2'
}

apply from: 'gradle/runtime-provenance.gradle'

def manifestAttributes = [
'Manifest-Version' : '1.0',
'Created-By' : 'Neuron Robotics Cooperative',
'Specification-Title' : 'LewisOmniscientDebugger',
'Specification-Version' : java.targetCompatibility,
'Specification-Vendor' : 'Neuron Robotics Cooperative',
'Implementation-Title' : 'LewisOmniscientDebugger',
'Implementation-Version' : java.targetCompatibility,
'Implementation-Vendor' : 'Neuron Robotics Cooperative',
'Main-Class' : 'com.lambda.Debugger.Debugger',
]

Expand All @@ -59,6 +60,9 @@ def launchProgramClasses = { File dir ->
def debuggerDebuggerGeneratedSourceDir = layout.buildDirectory.dir('generated/sources/debuggerDebugger/java')
def debuggerDebuggerMainClassesDir = layout.buildDirectory.dir('debuggerDebugger/mainClasses')
def debuggerDebuggerWorkDir = layout.buildDirectory.dir('debuggerDebugger/work')
def pristineRuntimeClassesDir = layout.buildDirectory.dir('runtime/pristineClasses')
def instrumentedRuntimeClassesDir = layout.buildDirectory.dir('runtime/instrumentedClasses')
def runtimeInstrumentationWorkDir = layout.buildDirectory.dir('runtime/work')
def debuggerDebuggerSourceReplacements = [
['com.lambda', 'lambda'],
['edu.insa.LSD', 'insa.LSD'],
Expand All @@ -82,6 +86,9 @@ sourceSets {
}
}

def compiledMainClassesDirs = files(sourceSets.main.output.classesDirs.files)
sourceSets.main.output.setClassesDirs(files(instrumentedRuntimeClassesDir))

tasks.register('generateDebuggerDebuggerSources') {
description = 'Generate the package-rewritten ODB sources used to debug ODB itself'
group = 'build'
Expand Down Expand Up @@ -130,17 +137,43 @@ tasks.register('prepareDebuggerDebuggerWorkDir') {
}
}

tasks.register('debugifyLaunchPrograms', JavaExec) {
tasks.register('stageRuntimeClasses', Sync) {
dependsOn tasks.named('compileJava')
classpath = sourceSets.main.runtimeClasspath
from compiledMainClassesDirs
into pristineRuntimeClassesDir
}

def debugifyLaunchPrograms = tasks.register('debugifyLaunchPrograms', JavaExec) {
dependsOn tasks.named('stageRuntimeClasses')
classpath = files(instrumentedRuntimeClassesDir) + configurations.runtimeClasspath
mainClass = 'com.lambda.Debugger.Debugify'
workingDir = runtimeInstrumentationWorkDir.get().asFile

inputs.dir(pristineRuntimeClassesDir)
inputs.file('.debuggerDefaults')
inputs.property('debugifyClassPatterns', debugifyClassPatterns)
outputs.dir(instrumentedRuntimeClassesDir)
outputs.dir(runtimeInstrumentationWorkDir)

def debugifyOutput = new ByteArrayOutputStream()
standardOutput = debugifyOutput
errorOutput = debugifyOutput

doFirst {
def dir = file("${layout.buildDirectory.get()}/classes/java/main/com/lambda/Debugger")
def classesDir = instrumentedRuntimeClassesDir.get().asFile
delete classesDir
copy {
from pristineRuntimeClassesDir
into classesDir
}
def workDir = runtimeInstrumentationWorkDir.get().asFile
delete workDir
workDir.mkdirs()
copy {
from '.debuggerDefaults'
into workDir
}
def dir = new File(classesDir, 'com/lambda/Debugger')
args = launchProgramClasses(dir)*.absolutePath
if (args.isEmpty()) {
throw new GradleException("No launch program classes matched for debugification in ${dir}")
Expand All @@ -159,6 +192,7 @@ tasks.register('debugifyLaunchPrograms', JavaExec) {
}
}
}
sourceSets.main.output.classesDirs.builtBy(debugifyLaunchPrograms)

tasks.register('debugifyDebuggerDebuggerLaunchPrograms', JavaExec) {
description = 'Pre-instrument launch programs in the package-rewritten ODB'
Expand Down Expand Up @@ -196,7 +230,7 @@ tasks.register('debugifyDebuggerDebuggerLaunchPrograms', JavaExec) {

tasks.register('copyMainClassesForDebuggerDebugger', Sync) {
dependsOn tasks.named('debugifyLaunchPrograms')
from sourceSets.main.output.classesDirs
from instrumentedRuntimeClassesDir
into debuggerDebuggerMainClassesDir
}

Expand Down Expand Up @@ -244,17 +278,26 @@ tasks.register('debugifyDebuggerDebuggerMainClasses', JavaExec) {

tasks.named('jar', Jar) {
dependsOn tasks.named('debugifyLaunchPrograms')
dependsOn tasks.named('processResources')
archiveFileName = 'LewisOmniscientDebugger.jar'
manifest.attributes(manifestAttributes)

duplicatesStrategy = DuplicatesStrategy.EXCLUDE
duplicatesStrategy = DuplicatesStrategy.FAIL

from {
configurations.runtimeClasspath.collect { dep ->
dep.isDirectory() ? dep : zipTree(dep)
}
configurations.runtimeClasspath.resolvedConfiguration.resolvedArtifacts
.sort { artifact ->
def id = artifact.moduleVersion.id
"${id.group}:${artifact.name}:${id.version}".toString()
}
.collect { zipTree(it.file) }
}

exclude 'com/lambda/tests/**'
exclude 'edu/insa/LSD/Test*.class'
exclude 'module-info.class'
exclude 'META-INF/versions/**'
exclude 'META-INF/LICENSE*'
exclude 'META-INF/NOTICE*'
exclude 'META-INF/MANIFEST.MF'
exclude 'META-INF/*.SF'
exclude 'META-INF/*.DSA'
Expand All @@ -269,17 +312,26 @@ tasks.register('debuggerDebuggerJar', Jar) {
archiveFileName = 'LewisOmniscientDebugger-debugger-debugger.jar'
manifest.attributes(manifestAttributes + ['Main-Class': 'lambda.Debugger.Debugger'])

duplicatesStrategy = DuplicatesStrategy.EXCLUDE
duplicatesStrategy = DuplicatesStrategy.FAIL

from(debuggerDebuggerMainClassesDir)
from(sourceSets.debuggerDebugger.output)
from(sourceSets.main.output.resourcesDir)
from {
configurations.runtimeClasspath.collect { dep ->
dep.isDirectory() ? dep : zipTree(dep)
}
configurations.runtimeClasspath.resolvedConfiguration.resolvedArtifacts
.sort { artifact ->
def id = artifact.moduleVersion.id
"${id.group}:${artifact.name}:${id.version}".toString()
}
.collect { zipTree(it.file) }
}

exclude 'com/lambda/tests/**'
exclude 'edu/insa/LSD/Test*.class'
exclude 'module-info.class'
exclude 'META-INF/versions/**'
exclude 'META-INF/LICENSE*'
exclude 'META-INF/NOTICE*'
exclude 'META-INF/MANIFEST.MF'
exclude 'META-INF/*.SF'
exclude 'META-INF/*.DSA'
Expand Down Expand Up @@ -308,3 +360,19 @@ tasks.register('verifyDemoRecordings', Test) {
testClassesDirs = sourceSets.test.output.classesDirs
include '**/VerifyRecordingTest.class'
}

tasks.register('verifyRuntimeTests', Test) {
description = 'Run finite ODB unit and adapter tests against the runtime JAR'
group = 'verification'
dependsOn tasks.named('jar')
dependsOn tasks.named('testClasses')

def jarFile = tasks.named('jar', Jar).get().archiveFile
def mainClasses = sourceSets.main.output.classesDirs.files
classpath = sourceSets.test.runtimeClasspath.filter { !mainClasses.contains(it) } + files(jarFile)
testClassesDirs = sourceSets.test.output.classesDirs
exclude '**/VerifyRecordingTest.class'
}

apply from: 'gradle/collection-provenance.gradle'
apply from: 'gradle/reproducible-release.gradle'
17 changes: 17 additions & 0 deletions docs/legal/COLLECTION-PROVENANCE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Collection compatibility provenance

Lewis ODB commit `48bbb7dfaf31b129906056b422c7b97f5dde77e0`
replaced the former collection implementation bodies with original
compatibility implementations based only on public Java 8 collection APIs.
The retained class names preserve ODB callers and serialized data.

`MyArrayList` remains ODB instrumentation machinery. `HashMapEq` preserves
identity-key behavior. `VectorD` preserves identity search and its
non-traversing debugger string while using `java.util.Vector` storage.

The historical prebuilt `LewisOmniscientDebugger-1.5.jar` was removed in
commit `0ffedc6`. `verifyCollectionProvenance` rejects that binary, former
proprietary notice text, or missing compatibility classes.

These changes are modifications under the repository's GPL terms. This record
describes project provenance and is not legal advice.
8 changes: 8 additions & 0 deletions gradle.lockfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# This is a Gradle generated file for dependency locking.
# Manual edits can break the build and are not advised.
# This file is expected to be part of source control.
commons-io:commons-io:2.21.0=runtimeClasspath
org.apache.bcel:bcel:6.12.0=runtimeClasspath
org.apache.commons:commons-lang3:3.20.0=runtimeClasspath
org.ow2.asm:asm:9.7.1=runtimeClasspath
empty=
10 changes: 10 additions & 0 deletions gradle/canonical-build.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
container.image=azul/zulu-openjdk:8u492@sha256:185e7ab12b3706a39a56b2d5c8921f36ddff8e1c95d704a0a2f22b7e35c611d1
environment.LC_ALL=C.UTF-8
environment.TZ=UTC
file.encoding=UTF-8
gradle.version=8.12.1
java.runtime.version=1.8.0_492-b09
java.vendor=Azul Systems, Inc.
java.version=1.8.0_492
os.arch=amd64
os.name=Linux
135 changes: 135 additions & 0 deletions gradle/collection-provenance.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
def arrayListHistoryClassesDir = layout.buildDirectory.dir('verification/array-list-history/classes')
def debuggerDebuggerGeneratedSourceDir = tasks.named('generateDebuggerDebuggerSources').map {
it.outputs.files.singleFile
}
tasks.register('prepareArrayListHistoryTarget', Sync) {
dependsOn tasks.named('testClasses')
from(sourceSets.test.output.classesDirs) {
include 'outside/ArrayListHistoryTarget.class'
}
into(arrayListHistoryClassesDir)
}

tasks.register('debugifyArrayListHistoryTarget', JavaExec) {
description = 'Pre-instrument the ArrayList history verification target'
group = 'verification'
dependsOn tasks.named('prepareArrayListHistoryTarget')
classpath = sourceSets.test.runtimeClasspath
mainClass = 'com.lambda.Debugger.Debugify'

def debugifyOutput = new ByteArrayOutputStream()
standardOutput = debugifyOutput
errorOutput = debugifyOutput

doFirst {
args = [new File(arrayListHistoryClassesDir.get().asFile,
'outside/ArrayListHistoryTarget.class').absolutePath]
}
doLast {
def output = debugifyOutput.toString('UTF-8')
if (!output.isBlank()) {
logger.lifecycle(output.trim())
}
if (executionResult.get().exitValue != 0 || !(output =~ /debugified 1 files\./).find()) {
throw new GradleException('Failed to instrument ArrayList history verification target')
}
}
}

tasks.named('verifyDemoRecordings', Test) {
dependsOn tasks.named('debugifyArrayListHistoryTarget')

def jarFile = tasks.named('jar', Jar).get().archiveFile
def mainClasses = sourceSets.main.output.classesDirs.files
classpath = files(arrayListHistoryClassesDir) +
sourceSets.test.runtimeClasspath.filter { !mainClasses.contains(it) } +
files(jarFile)
}

tasks.register('verifyCollectionProvenance') {
description = 'Verify retained ODB collection compatibility classes and release exclusions'
group = 'verification'
dependsOn tasks.named('jar')
dependsOn tasks.named('compileDebuggerDebuggerJava')
dependsOn tasks.named('debuggerDebuggerJar')

doLast {
def expectedClasses = [
'com/lambda/Debugger/HashMapEq.class',
'com/lambda/Debugger/MyAbstractCollection.class',
'com/lambda/Debugger/MyAbstractList.class',
'com/lambda/Debugger/MyArrayList.class',
'com/lambda/Debugger/MyCollections.class',
'com/lambda/Debugger/SubList.class',
'com/lambda/Debugger/VectorD.class',
]
def jarChecks = [
[
file: tasks.named('jar', Jar).get().archiveFile.get().asFile,
classes: expectedClasses,
],
[
file: tasks.named('debuggerDebuggerJar', Jar).get().archiveFile.get().asFile,
classes: expectedClasses + expectedClasses.collect {
it.replace('com/lambda/Debugger/', 'lambda/Debugger/')
},
],
]
jarChecks.each { check ->
def jarFile = check.file
def zip = new java.util.zip.ZipFile(jarFile)
try {
check.classes.each { entry ->
if (zip.getEntry(entry) == null) {
throw new GradleException("${jarFile.name} lacks retained compatibility class ${entry}")
}
}
if (zip.entries().find { it.name.endsWith('LewisOmniscientDebugger-1.5.jar') }) {
throw new GradleException("${jarFile.name} embeds the historical binary")
}
} finally {
zip.close()
}
}

def legacyJar = file('LewisOmniscientDebugger-1.5.jar')
if (legacyJar.exists()) {
throw new GradleException('Historical JAR must not remain in the source tree')
}

def rewrittenNames = [
'HashMapEq.java',
'VectorD.java',
'MyAbstractCollection.java',
'MyAbstractList.java',
'MyCollections.java',
]
def forbiddenPhrases = [
'Sun Microsystems',
'confidential and proprietary information',
'Use is subject to license terms',
]
def sourceRoots = [
file('src/main/java/com/lambda/Debugger'),
debuggerDebuggerGeneratedSourceDir.get().toPath()
.resolve('lambda/Debugger').toFile(),
]
sourceRoots.each { root ->
rewrittenNames.each { name ->
def source = new File(root, name)
if (!source.isFile()) {
throw new GradleException("Missing retained collection source ${source}")
}
forbiddenPhrases.each { phrase ->
if (source.getText('UTF-8').contains(phrase)) {
throw new GradleException("${source} retains prohibited copied notice text: ${phrase}")
}
}
}
}
}
}

tasks.named('check') {
dependsOn tasks.named('verifyCollectionProvenance')
}
Loading
Loading