mirror of
https://github.com/hneemann/Digital.git
synced 2025-09-15 07:48:29 -04:00
added a config file cache
external tools run in a own thread
This commit is contained in:
parent
eea5d81185
commit
3d361c8cd1
@ -2,7 +2,7 @@
|
|||||||
<ide name="BASYS3" clockGenerator="clockGenerator">
|
<ide name="BASYS3" clockGenerator="clockGenerator">
|
||||||
<commands>
|
<commands>
|
||||||
<command name="Export" requires="vhdl" />
|
<command name="Export" requires="vhdl" />
|
||||||
<command name="Export & Start Vivado" requires="vhdl" filter="true" gui="true" timeout="0">
|
<command name="Export & Start Vivado" requires="vhdl" filter="true" timeout="0">
|
||||||
<arg>vivado</arg>
|
<arg>vivado</arg>
|
||||||
<arg>vivado/<?=shortname?>.xpr</arg>
|
<arg>vivado/<?=shortname?>.xpr</arg>
|
||||||
</command>
|
</command>
|
||||||
|
@ -11,7 +11,6 @@ package de.neemann.digital.ide;
|
|||||||
public class Command {
|
public class Command {
|
||||||
private final String name;
|
private final String name;
|
||||||
private final boolean filter;
|
private final boolean filter;
|
||||||
private final boolean gui;
|
|
||||||
private final String requires;
|
private final String requires;
|
||||||
private final String[] args;
|
private final String[] args;
|
||||||
private final int timeout;
|
private final int timeout;
|
||||||
@ -32,7 +31,6 @@ public class Command {
|
|||||||
this.timeout = timeout;
|
this.timeout = timeout;
|
||||||
this.args = args;
|
this.args = args;
|
||||||
this.filter = filter;
|
this.filter = filter;
|
||||||
this.gui = gui;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -70,13 +68,6 @@ public class Command {
|
|||||||
return filter;
|
return filter;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @return true if application has a gui
|
|
||||||
*/
|
|
||||||
public boolean isGui() {
|
|
||||||
return gui;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return timeout in seconds
|
* @return timeout in seconds
|
||||||
*/
|
*/
|
||||||
|
48
src/main/java/de/neemann/digital/ide/ConfigCache.java
Normal file
48
src/main/java/de/neemann/digital/ide/ConfigCache.java
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2019 Helmut Neemann.
|
||||||
|
* Use of this source code is governed by the GPL v3 license
|
||||||
|
* that can be found in the LICENSE file.
|
||||||
|
*/
|
||||||
|
package de.neemann.digital.ide;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.HashMap;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper to avoid loading a config several times.
|
||||||
|
*/
|
||||||
|
class ConfigCache {
|
||||||
|
private final File initialFile;
|
||||||
|
private HashMap<String, Configuration> cache;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new instance
|
||||||
|
*
|
||||||
|
* @param initialFile the intial config file
|
||||||
|
*/
|
||||||
|
ConfigCache(File initialFile) {
|
||||||
|
this.initialFile = initialFile;
|
||||||
|
cache = new HashMap<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads the give config.
|
||||||
|
*
|
||||||
|
* @param filename the configs file name
|
||||||
|
* @return the config
|
||||||
|
* @throws IOException IOException
|
||||||
|
*/
|
||||||
|
Configuration getConfig(String filename) throws IOException {
|
||||||
|
if (initialFile == null)
|
||||||
|
throw new IOException("No initial config file given!");
|
||||||
|
|
||||||
|
|
||||||
|
Configuration c = cache.get(filename);
|
||||||
|
if (c == null) {
|
||||||
|
c = Configuration.load(new File(initialFile.getParentFile(), filename));
|
||||||
|
cache.put(filename, c);
|
||||||
|
}
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
}
|
@ -80,7 +80,6 @@ public final class Configuration {
|
|||||||
xStream.aliasAttribute(Command.class, "name", "name");
|
xStream.aliasAttribute(Command.class, "name", "name");
|
||||||
xStream.aliasAttribute(Command.class, "requires", "requires");
|
xStream.aliasAttribute(Command.class, "requires", "requires");
|
||||||
xStream.aliasAttribute(Command.class, "filter", "filter");
|
xStream.aliasAttribute(Command.class, "filter", "filter");
|
||||||
xStream.aliasAttribute(Command.class, "gui", "gui");
|
|
||||||
xStream.aliasAttribute(Command.class, "timeout", "timeout");
|
xStream.aliasAttribute(Command.class, "timeout", "timeout");
|
||||||
xStream.addImplicitCollection(Command.class, "args", "arg", String.class);
|
xStream.addImplicitCollection(Command.class, "args", "arg", String.class);
|
||||||
xStream.alias("file", FileToCreate.class);
|
xStream.alias("file", FileToCreate.class);
|
||||||
@ -160,33 +159,17 @@ public final class Configuration {
|
|||||||
return menu;
|
return menu;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void checkFilesToCreate(File fileToExecute, HDLModel hdlModel) throws HGSEvalException, IOException, ParserException {
|
private final class ExecuteAction extends AbstractAction {
|
||||||
Context context = createContext(fileToExecute, hdlModel);
|
private final Command command;
|
||||||
|
|
||||||
if (files != null)
|
private ExecuteAction(Command command) {
|
||||||
for (FileToCreate f : files) {
|
super(command.getName());
|
||||||
context.clearOutput();
|
this.command = command;
|
||||||
Parser p = new Parser(f.getName());
|
|
||||||
p.parse().execute(context);
|
|
||||||
File filename = new File(fileToExecute.getParent(), context.toString());
|
|
||||||
|
|
||||||
if (f.isOverwrite() || !filename.exists())
|
|
||||||
createFile(filename, resolveFileContent(f), context);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void createFile(File filename, FileToCreate f, Context context) throws IOException, HGSEvalException, ParserException {
|
@Override
|
||||||
Parser p;
|
public void actionPerformed(ActionEvent actionEvent) {
|
||||||
String content = f.getContent();
|
executeCommand(command, this);
|
||||||
if (f.isFilter()) {
|
|
||||||
context.clearOutput();
|
|
||||||
p = new Parser(content);
|
|
||||||
p.parse().execute(context);
|
|
||||||
content = context.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
try (OutputStream out = getIoInterface().getOutputStream(filename)) {
|
|
||||||
out.write(content.getBytes());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -249,15 +232,20 @@ public final class Configuration {
|
|||||||
*
|
*
|
||||||
* @param command the command
|
* @param command the command
|
||||||
*/
|
*/
|
||||||
void executeCommand(Command command) {
|
Thread executeCommand(Command command, Action action) {
|
||||||
File digFile = filenameProvider.getCurrentFilename();
|
File digFile = filenameProvider.getCurrentFilename();
|
||||||
if (digFile != null) {
|
if (digFile != null) {
|
||||||
try {
|
try {
|
||||||
|
HDLModel hdlModel;
|
||||||
HDLModel hdlModel = null;
|
|
||||||
if (command.needsHDL())
|
if (command.needsHDL())
|
||||||
hdlModel = writeHDL(command.getHDL(), digFile);
|
hdlModel = writeHDL(command.getHDL(), digFile);
|
||||||
|
else
|
||||||
|
hdlModel = null;
|
||||||
|
|
||||||
|
if (action != null)
|
||||||
|
action.setEnabled(false);
|
||||||
|
Thread t = new Thread(() -> {
|
||||||
|
try {
|
||||||
checkFilesToCreate(digFile, hdlModel);
|
checkFilesToCreate(digFile, hdlModel);
|
||||||
|
|
||||||
String[] args = command.getArgs();
|
String[] args = command.getArgs();
|
||||||
@ -271,46 +259,73 @@ public final class Configuration {
|
|||||||
args[i] = context.toString();
|
args[i] = context.toString();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
getIoInterface().startProcess(command, digFile.getParentFile(), command.isGui(), args);
|
getIoInterface().startProcess(command, digFile.getParentFile(), args);
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
getIoInterface().showError(command, e);
|
getIoInterface().showError(command, e);
|
||||||
|
} finally {
|
||||||
|
if (action != null)
|
||||||
|
SwingUtilities.invokeLater(() -> action.setEnabled(true));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
t.setDaemon(true);
|
||||||
|
t.start();
|
||||||
|
return t;
|
||||||
|
} catch (Exception e) {
|
||||||
|
getIoInterface().showError(command, e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private FileToCreate resolveFileContent(FileToCreate f) throws IOException {
|
private void checkFilesToCreate(File fileToExecute, HDLModel hdlModel) throws HGSEvalException, IOException, ParserException {
|
||||||
|
Context context = createContext(fileToExecute, hdlModel);
|
||||||
|
|
||||||
|
if (files != null) {
|
||||||
|
ConfigCache configCache = new ConfigCache(origin);
|
||||||
|
for (FileToCreate f : files) {
|
||||||
|
context.clearOutput();
|
||||||
|
Parser p = new Parser(f.getName());
|
||||||
|
p.parse().execute(context);
|
||||||
|
File filename = new File(fileToExecute.getParent(), context.toString());
|
||||||
|
|
||||||
|
if (f.isOverwrite() || !filename.exists())
|
||||||
|
createFile(filename, resolveFileContent(f, configCache), context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void createFile(File filename, FileToCreate f, Context context) throws IOException, HGSEvalException, ParserException {
|
||||||
|
Parser p;
|
||||||
|
String content = f.getContent();
|
||||||
|
if (f.isFilter()) {
|
||||||
|
context.clearOutput();
|
||||||
|
p = new Parser(content);
|
||||||
|
p.parse().execute(context);
|
||||||
|
content = context.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
try (OutputStream out = getIoInterface().getOutputStream(filename)) {
|
||||||
|
out.write(content.getBytes());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private FileToCreate resolveFileContent(FileToCreate f, ConfigCache configCache) throws IOException {
|
||||||
if (f.hasContent())
|
if (f.hasContent())
|
||||||
return f;
|
return f;
|
||||||
|
|
||||||
if (origin == null)
|
Configuration c = configCache.getConfig(f.getReferenceFilename());
|
||||||
throw new IOException("no origin file given");
|
return c.getFileById(f.getReferenceId(), configCache);
|
||||||
|
|
||||||
Configuration c = Configuration.load(new File(origin.getParentFile(), f.getReferenceFilename()));
|
|
||||||
return c.getFileById(f.getReferenceId());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private FileToCreate getFileById(String referenceId) throws IOException {
|
private FileToCreate getFileById(String referenceId, ConfigCache configCache) throws IOException {
|
||||||
for (FileToCreate f : files)
|
for (FileToCreate f : files)
|
||||||
if (referenceId.equals(f.getId()))
|
if (referenceId.equals(f.getId()))
|
||||||
return resolveFileContent(f);
|
return resolveFileContent(f, configCache);
|
||||||
throw new IOException("no file with id " + referenceId + " given");
|
throw new IOException("no file with id " + referenceId + " given");
|
||||||
}
|
}
|
||||||
|
|
||||||
private final class ExecuteAction extends AbstractAction {
|
|
||||||
private final Command command;
|
|
||||||
|
|
||||||
private ExecuteAction(Command command) {
|
|
||||||
super(command.getName());
|
|
||||||
this.command = command;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void actionPerformed(ActionEvent actionEvent) {
|
|
||||||
executeCommand(command);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ArrayList<Command> getCommands() {
|
ArrayList<Command> getCommands() {
|
||||||
return commands;
|
return commands;
|
||||||
}
|
}
|
||||||
@ -374,11 +389,10 @@ public final class Configuration {
|
|||||||
*
|
*
|
||||||
* @param command the command started
|
* @param command the command started
|
||||||
* @param dir the folder to start the process in
|
* @param dir the folder to start the process in
|
||||||
* @param gui true if app has a gui
|
|
||||||
* @param args the arguments
|
* @param args the arguments
|
||||||
* @throws IOException IOException
|
* @throws IOException IOException
|
||||||
*/
|
*/
|
||||||
void startProcess(Command command, File dir, boolean gui, String[] args) throws IOException;
|
void startProcess(Command command, File dir, String[] args) throws IOException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Shows an error message
|
* Shows an error message
|
||||||
@ -402,29 +416,17 @@ public final class Configuration {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void startProcess(Command command, File dir, boolean gui, String[] args) throws IOException {
|
public void startProcess(Command command, File dir, String[] args) throws IOException {
|
||||||
OSExecute os = new OSExecute(args)
|
String consoleOut = new OSExecute(args)
|
||||||
.setTimeOutSec(command.getTimeout())
|
.setTimeOutSec(command.getTimeout())
|
||||||
.setWorkingDir(dir);
|
.setWorkingDir(dir)
|
||||||
if (gui)
|
.startAndWait();
|
||||||
os.startInThread(new OSExecute.ProcessCallback() {
|
|
||||||
@Override
|
|
||||||
public void processTerminated(String consoleOut) {
|
|
||||||
LOGGER.info("process '" + command.getName() + "' says:\n" + consoleOut);
|
LOGGER.info("process '" + command.getName() + "' says:\n" + consoleOut);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void exception(Exception e) {
|
|
||||||
showError(command, e);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
else
|
|
||||||
os.startAndWait();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void showError(Command command, Exception e) {
|
public void showError(Command command, Exception e) {
|
||||||
new ErrorMessage(Lang.get("msg_errorStartCommand_N", command.getName())).addCause(e).show();
|
SwingUtilities.invokeLater(new ErrorMessage(Lang.get("msg_errorStartCommand_N", command.getName())).addCause(e));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -19,7 +19,7 @@ import java.util.HashMap;
|
|||||||
|
|
||||||
public class ConfigurationTest extends TestCase {
|
public class ConfigurationTest extends TestCase {
|
||||||
|
|
||||||
public void testStart() throws IOException, ElementNotFoundException, PinException, NodeException {
|
public void testStart() throws IOException, ElementNotFoundException, PinException, NodeException, InterruptedException {
|
||||||
String xml = "<ide name=\"APIO\">\n" +
|
String xml = "<ide name=\"APIO\">\n" +
|
||||||
" <commands>\n" +
|
" <commands>\n" +
|
||||||
" <command name=\"build\" requires=\"verilog\" filter=\"false\">\n" +
|
" <command name=\"build\" requires=\"verilog\" filter=\"false\">\n" +
|
||||||
@ -44,7 +44,7 @@ public class ConfigurationTest extends TestCase {
|
|||||||
ArrayList<Command> commands = c.getCommands();
|
ArrayList<Command> commands = c.getCommands();
|
||||||
assertEquals(2, commands.size());
|
assertEquals(2, commands.size());
|
||||||
|
|
||||||
c.executeCommand(commands.get(0));
|
c.executeCommand(commands.get(0), null).join();
|
||||||
|
|
||||||
assertEquals(1, fileWriter.files.size());
|
assertEquals(1, fileWriter.files.size());
|
||||||
assertTrue(fileWriter.files.containsKey("z/test.v"));
|
assertTrue(fileWriter.files.containsKey("z/test.v"));
|
||||||
@ -54,7 +54,7 @@ public class ConfigurationTest extends TestCase {
|
|||||||
assertEquals("[make]", Arrays.toString(fileWriter.commands.get(0).args));
|
assertEquals("[make]", Arrays.toString(fileWriter.commands.get(0).args));
|
||||||
|
|
||||||
fileWriter.clear();
|
fileWriter.clear();
|
||||||
c.executeCommand(commands.get(1));
|
c.executeCommand(commands.get(1), null).join();
|
||||||
|
|
||||||
assertEquals(1, fileWriter.files.size());
|
assertEquals(1, fileWriter.files.size());
|
||||||
assertTrue(fileWriter.files.containsKey("z/test.v"));
|
assertTrue(fileWriter.files.containsKey("z/test.v"));
|
||||||
@ -64,7 +64,7 @@ public class ConfigurationTest extends TestCase {
|
|||||||
assertEquals("[make, z/test.v]", Arrays.toString(fileWriter.commands.get(0).args));
|
assertEquals("[make, z/test.v]", Arrays.toString(fileWriter.commands.get(0).args));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testFileWriter() throws IOException, ElementNotFoundException, PinException, NodeException {
|
public void testFileWriter() throws IOException, ElementNotFoundException, PinException, NodeException, InterruptedException {
|
||||||
String xml = "<ide name=\"APIO\">\n" +
|
String xml = "<ide name=\"APIO\">\n" +
|
||||||
" <commands>\n" +
|
" <commands>\n" +
|
||||||
" <command name=\"build\" requires=\"verilog\" filter=\"false\">\n" +
|
" <command name=\"build\" requires=\"verilog\" filter=\"false\">\n" +
|
||||||
@ -96,7 +96,7 @@ public class ConfigurationTest extends TestCase {
|
|||||||
ArrayList<Command> commands = c.getCommands();
|
ArrayList<Command> commands = c.getCommands();
|
||||||
assertEquals(1, commands.size());
|
assertEquals(1, commands.size());
|
||||||
|
|
||||||
c.executeCommand(commands.get(0));
|
c.executeCommand(commands.get(0), null).join();
|
||||||
|
|
||||||
assertEquals(4, fileWriter.files.size());
|
assertEquals(4, fileWriter.files.size());
|
||||||
assertEquals("deal with <?=path?>", fileWriter.files.get("z/file1").toString());
|
assertEquals("deal with <?=path?>", fileWriter.files.get("z/file1").toString());
|
||||||
@ -117,7 +117,7 @@ public class ConfigurationTest extends TestCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void startProcess(Command command, File dir, boolean gui, String[] args) {
|
public void startProcess(Command command, File dir, String[] args) {
|
||||||
commands.add(new StartedCommand(dir, args));
|
commands.add(new StartedCommand(dir, args));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -17,7 +17,7 @@ import java.io.IOException;
|
|||||||
|
|
||||||
public class ReferenceTest extends TestCase {
|
public class ReferenceTest extends TestCase {
|
||||||
|
|
||||||
public void testRef() throws IOException, ElementNotFoundException, PinException, NodeException {
|
public void testRef() throws IOException, ElementNotFoundException, PinException, NodeException, InterruptedException {
|
||||||
File f = new File(Resources.getRoot(),"ide/main.xml");
|
File f = new File(Resources.getRoot(),"ide/main.xml");
|
||||||
ToBreakRunner br = new ToBreakRunner(new File(Resources.getRoot(), "dig/hdl/negSimple.dig"));
|
ToBreakRunner br = new ToBreakRunner(new File(Resources.getRoot(), "dig/hdl/negSimple.dig"));
|
||||||
Configuration c = Configuration.load(f)
|
Configuration c = Configuration.load(f)
|
||||||
@ -26,7 +26,7 @@ public class ReferenceTest extends TestCase {
|
|||||||
.setCircuitProvider(br::getCircuit);
|
.setCircuitProvider(br::getCircuit);
|
||||||
final ConfigurationTest.TestIOInterface ioInterface = new ConfigurationTest.TestIOInterface();
|
final ConfigurationTest.TestIOInterface ioInterface = new ConfigurationTest.TestIOInterface();
|
||||||
c.setIoInterface(ioInterface);
|
c.setIoInterface(ioInterface);
|
||||||
c.executeCommand(c.getCommands().get(0));
|
c.executeCommand(c.getCommands().get(0),null).join();
|
||||||
|
|
||||||
assertEquals("Test content", ioInterface.getFiles().get("z/test1.txt").toString());
|
assertEquals("Test content", ioInterface.getFiles().get("z/test1.txt").toString());
|
||||||
assertEquals("deep content", ioInterface.getFiles().get("z/test2.txt").toString());
|
assertEquals("deep content", ioInterface.getFiles().get("z/test2.txt").toString());
|
||||||
|
Loading…
x
Reference in New Issue
Block a user