Skip to content
Draft
Show file tree
Hide file tree
Changes from 20 commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
ab488b4
feat: add optional parallel task execution in MessageCodeGenerator
Copilot May 20, 2026
fde52ce
feat: log parallel worker count for generator tasks
Copilot May 20, 2026
d9804f4
feat: enable parallel generation by default
Copilot May 21, 2026
3703f5f
feat: enable aggregated mojo generation tasks
Copilot May 21, 2026
1804050
feat: log task start and finish progress
Copilot May 21, 2026
8e61616
test: remove order-dependent parallel finish assertion
Copilot May 21, 2026
1b325a9
test: make parallel log capture thread-safe in flaky test
Copilot May 21, 2026
094eb06
Merge branch 'master' into copilot/add-parallel-execution-option
chrjohn May 22, 2026
b087252
Merge branch 'master' into copilot/add-parallel-execution-option
chrjohn May 22, 2026
99c98ca
test: add parallel same-directory field generation race coverage
Copilot May 23, 2026
3caae82
refactor: remove per-output-file write synchronization from generator
Copilot May 23, 2026
53358c4
test: increase ParallelFieldGenerationRaceTest stress level
Copilot May 26, 2026
9c8e038
feat: add optional parallel thread count for code generator
Copilot May 26, 2026
09795a5
build: enable verbose javac diagnostics
Copilot May 26, 2026
d08f2a6
ci: print generated sources on compile errors
Copilot May 27, 2026
f16e57e
Add deterministic concurrent field-writer race test seam
Copilot May 29, 2026
2a8eb41
Fix race test: give each parallel task its own output directory
Copilot May 29, 2026
c39dfd1
Add deterministic shared-output IDSource race regression test
Copilot May 31, 2026
7934b74
Replace race test with new shared-output reproduction
Copilot Jun 1, 2026
ce79588
Limit race test mismatch output to first class
Copilot Jun 1, 2026
13e20b8
Set race test dictionary minor versions per review
Copilot Jun 1, 2026
bb253a5
Address review feedback on golden source and compiler args
Copilot Jun 1, 2026
8fb097a
Force deterministic race in ParallelFieldGenerationRaceTest via Cycli…
Copilot Jun 1, 2026
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
45 changes: 43 additions & 2 deletions .github/workflows/maven.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,27 @@ jobs:
java-version: ${{ matrix.java }}
cache: 'maven'
- name: Test with Maven
run: ./mvnw install -B -V -D"maven.javadoc.skip"="true" -P"skipBundlePlugin,minimal-fix-latest" -D"java.util.logging.config.file"="${{github.workspace}}/quickfixj-core/src/test/resources/logging.properties" -D"http.keepAlive"="false" -D"maven.wagon.http.pool"="false" -D"maven.wagon.httpconnectionManager.ttlSeconds"="120"
shell: bash
run: |
set +e
log_file="${RUNNER_TEMP}/maven-build.log"
./mvnw install -B -V -D"maven.javadoc.skip"="true" -P"skipBundlePlugin,minimal-fix-latest" -D"java.util.logging.config.file"="${{github.workspace}}/quickfixj-core/src/test/resources/logging.properties" -D"http.keepAlive"="false" -D"maven.wagon.http.pool"="false" -D"maven.wagon.httpconnectionManager.ttlSeconds"="120" 2>&1 | tee "${log_file}"
status=${PIPESTATUS[0]}

if [ "${status}" -ne 0 ] && grep -q "COMPILATION ERROR" "${log_file}"; then
grep -Eo '/[^[:space:]]+\.java:\[[0-9]+,[0-9]+\]' "${log_file}" \
| sed -E 's/:\[[0-9]+,[0-9]+\]$//' \
| sort -u \
| while IFS= read -r file; do
if [ -f "${file}" ]; then
echo "::group::Contents of ${file}"
cat "${file}"
echo "::endgroup::"
fi
done
fi

exit "${status}"

test-windows:
runs-on: ${{ matrix.os }}
Expand All @@ -53,4 +73,25 @@ jobs:
java-version: ${{ matrix.java }}
cache: 'maven'
- name: Test with Maven on Windows
run: ./mvnw.cmd install -B -V -D"maven.javadoc.skip"="true" -P"skipBundlePlugin,minimal-fix-latest" -D"java.util.logging.config.file"="${{github.workspace}}/quickfixj-core/src/test/resources/logging.properties" -D"http.keepAlive"="false" -D"maven.wagon.http.pool"="false" -D"maven.wagon.httpconnectionManager.ttlSeconds"="120"
shell: pwsh
run: |
$logFile = Join-Path $env:RUNNER_TEMP "maven-build.log"
& ./mvnw.cmd install -B -V -D"maven.javadoc.skip"="true" -P"skipBundlePlugin,minimal-fix-latest" -D"java.util.logging.config.file"="${{github.workspace}}/quickfixj-core/src/test/resources/logging.properties" -D"http.keepAlive"="false" -D"maven.wagon.http.pool"="false" -D"maven.wagon.httpconnectionManager.ttlSeconds"="120" 2>&1 | Tee-Object -FilePath $logFile
$status = $LASTEXITCODE

if ($status -ne 0 -and (Select-String -Path $logFile -Pattern 'COMPILATION ERROR' -Quiet)) {
$files = Select-String -Path $logFile -Pattern '(?<file>(?:[A-Za-z]:)?[\\/][^:\s]+\.java):\[\d+,\d+\]' -AllMatches |
ForEach-Object { $_.Matches } |
ForEach-Object { $_.Groups['file'].Value } |
Sort-Object -Unique

foreach ($file in $files) {
if (Test-Path -Path $file) {
Write-Host "::group::Contents of $file"
Get-Content -Path $file
Write-Host "::endgroup::"
}
}
}

exit $status
3 changes: 3 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,9 @@
<forceLegacyJavacApi>true</forceLegacyJavacApi> <!-- https://bugs.openjdk.java.net/browse/JDK-8216202 -->
<meminitial>2g</meminitial>
<maxmem>4g</maxmem>
<compilerArgs>
<arg>-Xdiags:verbose</arg>
</compilerArgs>
Comment thread
chrjohn marked this conversation as resolved.
Outdated
</configuration>
</plugin>
<plugin>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@
import org.apache.maven.plugins.annotations.LifecyclePhase;

import java.io.File;
import java.util.ArrayList;
import java.util.List;

/**
* A mojo that uses the quickfix code generator to generate
Expand All @@ -45,6 +47,12 @@ public class GenerateMojo extends AbstractMojo {
@Parameter(defaultValue="${basedir}/src/main/quickfixj/dictionary/FIX44.xml")
private File dictFile;

/**
* Optional list of dictionaries/tasks to generate in a single execution.
*/
@Parameter
private List<GeneratorTask> tasks;

/**
* The source directory containing *.xsd files.
*/
Expand Down Expand Up @@ -72,7 +80,7 @@ public class GenerateMojo extends AbstractMojo {
/**
* The package for the generated source.
*/
@Parameter(required = true)
@Parameter
private String packaging;

/**
Expand Down Expand Up @@ -122,29 +130,8 @@ public void execute() throws MojoExecutionException {
}
generator.setLog(getLog());

MessageCodeGenerator.Task task = new MessageCodeGenerator.Task();
if (getLog().isInfoEnabled()) {
getLog().info("Initialising code generator task");
}

if (dictFile != null && dictFile.exists()) {
task.setSpecification(dictFile);
} else {
getLog().error("Cannot find file " + dictFile);
throw new MojoExecutionException("File could not be found or was NULL!");
}

log("Processing " + dictFile);
task.setName(dictFile.getName());
task.setTransformDirectory(schemaDirectory);
task.setMessagePackage(packaging);
task.setOutputBaseDirectory(outputDirectory);
task.setFieldPackage(fieldPackage);
task.setUtcTimestampPrecision(utcTimestampPrecision);
task.setOverwrite(overwrite);
task.setOrderedFields(orderedFields);
task.setDecimalGenerated(decimal);
generator.generate(task);
List<MessageCodeGenerator.Task> generationTasks = createGenerationTasks();
generator.generate(generationTasks);
} catch (Throwable t) {
throw new MojoExecutionException("QuickFIX/J code generator execution failed", t);
}
Expand All @@ -163,6 +150,135 @@ private void log(final String msg) {
getLog().info(msg);
}

private List<MessageCodeGenerator.Task> createGenerationTasks() throws MojoExecutionException {
List<GeneratorTask> configuredTasks;
if (tasks == null || tasks.isEmpty()) {
configuredTasks = new ArrayList<>();
GeneratorTask singleTask = new GeneratorTask();
singleTask.setDictFile(dictFile);
singleTask.setPackaging(packaging);
singleTask.setFieldPackage(fieldPackage);
singleTask.setUtcTimestampPrecision(utcTimestampPrecision);
singleTask.setOverwrite(overwrite);
singleTask.setOrderedFields(orderedFields);
singleTask.setDecimal(decimal);
configuredTasks.add(singleTask);
} else {
configuredTasks = tasks;
}

List<MessageCodeGenerator.Task> generationTasks = new ArrayList<>(configuredTasks.size());
for (GeneratorTask configuredTask : configuredTasks) {
MessageCodeGenerator.Task task = new MessageCodeGenerator.Task();
if (getLog().isInfoEnabled()) {
getLog().info("Initialising code generator task");
}

if (configuredTask.getDictFile() != null && configuredTask.getDictFile().exists()) {
task.setSpecification(configuredTask.getDictFile());
} else {
getLog().error("Cannot find file " + configuredTask.getDictFile());
throw new MojoExecutionException("File could not be found or was NULL!");
}
if (configuredTask.getPackaging() == null || configuredTask.getPackaging().isEmpty()) {
throw new MojoExecutionException("Packaging could not be found or was NULL!");
}

log("Processing " + configuredTask.getDictFile());
task.setName(configuredTask.getDictFile().getName());
task.setTransformDirectory(schemaDirectory);
task.setMessagePackage(configuredTask.getPackaging());
task.setOutputBaseDirectory(outputDirectory);
task.setFieldPackage(configuredTask.getFieldPackage() != null ? configuredTask.getFieldPackage() : fieldPackage);
task.setUtcTimestampPrecision(configuredTask.getUtcTimestampPrecision() != null
? configuredTask.getUtcTimestampPrecision() : utcTimestampPrecision);
task.setOverwrite(configuredTask.getOverwrite() != null ? configuredTask.getOverwrite() : overwrite);
task.setOrderedFields(configuredTask.getOrderedFields() != null ? configuredTask.getOrderedFields() : orderedFields);
task.setDecimalGenerated(configuredTask.getDecimal() != null ? configuredTask.getDecimal() : decimal);
generationTasks.add(task);
}
return generationTasks;
}

public static class GeneratorTask {
@Parameter(required = true)
private File dictFile;

@Parameter(required = true)
private String packaging;

@Parameter
private String fieldPackage;

@Parameter
private String utcTimestampPrecision;

@Parameter
private Boolean overwrite;

@Parameter
private Boolean orderedFields;

@Parameter
private Boolean decimal;

public File getDictFile() {
return dictFile;
}

public void setDictFile(File dictFile) {
this.dictFile = dictFile;
}

public String getPackaging() {
return packaging;
}

public void setPackaging(String packaging) {
this.packaging = packaging;
}

public String getFieldPackage() {
return fieldPackage;
}

public void setFieldPackage(String fieldPackage) {
this.fieldPackage = fieldPackage;
}

public String getUtcTimestampPrecision() {
return utcTimestampPrecision;
}

public void setUtcTimestampPrecision(String utcTimestampPrecision) {
this.utcTimestampPrecision = utcTimestampPrecision;
}

public Boolean getOverwrite() {
return overwrite;
}

public void setOverwrite(Boolean overwrite) {
this.overwrite = overwrite;
}

public Boolean getOrderedFields() {
return orderedFields;
}

public void setOrderedFields(Boolean orderedFields) {
this.orderedFields = orderedFields;
}

public Boolean getDecimal() {
return decimal;
}

public void setDecimal(Boolean decimal) {
this.decimal = decimal;
}
}

/**
* Returns the destination directory to used during code generation.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,14 +34,14 @@ public void setLog(Log log) {
}

protected void logInfo(String msg) {
log.info(msg);
log.info(formatLogMessage(msg));
}

protected void logDebug(String msg) {
log.debug(msg);
log.debug(formatLogMessage(msg));
}

protected void logError(String msg, Throwable e) {
log.error(msg, e);
log.error(formatLogMessage(msg), e);
}
}
Loading
Loading