added a config file cache

external tools run in a own thread
This commit is contained in:
hneemann 2019-05-17 12:52:29 +02:00
parent eea5d81185
commit 3d361c8cd1
6 changed files with 141 additions and 100 deletions

View File

@ -2,7 +2,7 @@
<ide name="BASYS3" clockGenerator="clockGenerator">
<commands>
<command name="Export" requires="vhdl" />
<command name="Export &amp; Start Vivado" requires="vhdl" filter="true" gui="true" timeout="0">
<command name="Export &amp; Start Vivado" requires="vhdl" filter="true" timeout="0">
<arg>vivado</arg>
<arg>vivado/&lt;?=shortname?&gt;.xpr</arg>
</command>

View File

@ -11,7 +11,6 @@ package de.neemann.digital.ide;
public class Command {
private final String name;
private final boolean filter;
private final boolean gui;
private final String requires;
private final String[] args;
private final int timeout;
@ -32,7 +31,6 @@ public class Command {
this.timeout = timeout;
this.args = args;
this.filter = filter;
this.gui = gui;
}
/**
@ -70,13 +68,6 @@ public class Command {
return filter;
}
/**
* @return true if application has a gui
*/
public boolean isGui() {
return gui;
}
/**
* @return timeout in seconds
*/

View 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;
}
}

View File

@ -80,7 +80,6 @@ public final class Configuration {
xStream.aliasAttribute(Command.class, "name", "name");
xStream.aliasAttribute(Command.class, "requires", "requires");
xStream.aliasAttribute(Command.class, "filter", "filter");
xStream.aliasAttribute(Command.class, "gui", "gui");
xStream.aliasAttribute(Command.class, "timeout", "timeout");
xStream.addImplicitCollection(Command.class, "args", "arg", String.class);
xStream.alias("file", FileToCreate.class);
@ -160,33 +159,17 @@ public final class Configuration {
return menu;
}
private void checkFilesToCreate(File fileToExecute, HDLModel hdlModel) throws HGSEvalException, IOException, ParserException {
Context context = createContext(fileToExecute, hdlModel);
private final class ExecuteAction extends AbstractAction {
private final Command command;
if (files != null)
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), 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();
private ExecuteAction(Command command) {
super(command.getName());
this.command = command;
}
try (OutputStream out = getIoInterface().getOutputStream(filename)) {
out.write(content.getBytes());
@Override
public void actionPerformed(ActionEvent actionEvent) {
executeCommand(command, this);
}
}
@ -249,68 +232,100 @@ public final class Configuration {
*
* @param command the command
*/
void executeCommand(Command command) {
Thread executeCommand(Command command, Action action) {
File digFile = filenameProvider.getCurrentFilename();
if (digFile != null) {
try {
HDLModel hdlModel = null;
HDLModel hdlModel;
if (command.needsHDL())
hdlModel = writeHDL(command.getHDL(), digFile);
else
hdlModel = null;
checkFilesToCreate(digFile, hdlModel);
if (action != null)
action.setEnabled(false);
Thread t = new Thread(() -> {
try {
checkFilesToCreate(digFile, hdlModel);
String[] args = command.getArgs();
if (args != null) {
if (command.isFilter()) {
final int argCount = command.getArgs().length;
Context context = createContext(digFile, hdlModel);
for (int i = 0; i < argCount; i++) {
context.clearOutput();
new Parser(args[i]).parse().execute(context);
args[i] = context.toString();
String[] args = command.getArgs();
if (args != null) {
if (command.isFilter()) {
final int argCount = command.getArgs().length;
Context context = createContext(digFile, hdlModel);
for (int i = 0; i < argCount; i++) {
context.clearOutput();
new Parser(args[i]).parse().execute(context);
args[i] = context.toString();
}
}
getIoInterface().startProcess(command, digFile.getParentFile(), args);
}
} catch (Exception e) {
getIoInterface().showError(command, e);
} finally {
if (action != null)
SwingUtilities.invokeLater(() -> action.setEnabled(true));
}
getIoInterface().startProcess(command, digFile.getParentFile(), command.isGui(), args);
}
});
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())
return f;
if (origin == null)
throw new IOException("no origin file given");
Configuration c = Configuration.load(new File(origin.getParentFile(), f.getReferenceFilename()));
return c.getFileById(f.getReferenceId());
Configuration c = configCache.getConfig(f.getReferenceFilename());
return c.getFileById(f.getReferenceId(), configCache);
}
private FileToCreate getFileById(String referenceId) throws IOException {
private FileToCreate getFileById(String referenceId, ConfigCache configCache) throws IOException {
for (FileToCreate f : files)
if (referenceId.equals(f.getId()))
return resolveFileContent(f);
return resolveFileContent(f, configCache);
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() {
return commands;
}
@ -374,11 +389,10 @@ public final class Configuration {
*
* @param command the command started
* @param dir the folder to start the process in
* @param gui true if app has a gui
* @param args the arguments
* @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
@ -402,29 +416,17 @@ public final class Configuration {
}
@Override
public void startProcess(Command command, File dir, boolean gui, String[] args) throws IOException {
OSExecute os = new OSExecute(args)
public void startProcess(Command command, File dir, String[] args) throws IOException {
String consoleOut = new OSExecute(args)
.setTimeOutSec(command.getTimeout())
.setWorkingDir(dir);
if (gui)
os.startInThread(new OSExecute.ProcessCallback() {
@Override
public void processTerminated(String consoleOut) {
LOGGER.info("process '" + command.getName() + "' says:\n" + consoleOut);
}
@Override
public void exception(Exception e) {
showError(command, e);
}
});
else
os.startAndWait();
.setWorkingDir(dir)
.startAndWait();
LOGGER.info("process '" + command.getName() + "' says:\n" + consoleOut);
}
@Override
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));
}
}

View File

@ -19,7 +19,7 @@ import java.util.HashMap;
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" +
" <commands>\n" +
" <command name=\"build\" requires=\"verilog\" filter=\"false\">\n" +
@ -44,7 +44,7 @@ public class ConfigurationTest extends TestCase {
ArrayList<Command> commands = c.getCommands();
assertEquals(2, commands.size());
c.executeCommand(commands.get(0));
c.executeCommand(commands.get(0), null).join();
assertEquals(1, fileWriter.files.size());
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));
fileWriter.clear();
c.executeCommand(commands.get(1));
c.executeCommand(commands.get(1), null).join();
assertEquals(1, fileWriter.files.size());
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));
}
public void testFileWriter() throws IOException, ElementNotFoundException, PinException, NodeException {
public void testFileWriter() throws IOException, ElementNotFoundException, PinException, NodeException, InterruptedException {
String xml = "<ide name=\"APIO\">\n" +
" <commands>\n" +
" <command name=\"build\" requires=\"verilog\" filter=\"false\">\n" +
@ -96,7 +96,7 @@ public class ConfigurationTest extends TestCase {
ArrayList<Command> commands = c.getCommands();
assertEquals(1, commands.size());
c.executeCommand(commands.get(0));
c.executeCommand(commands.get(0), null).join();
assertEquals(4, fileWriter.files.size());
assertEquals("deal with <?=path?>", fileWriter.files.get("z/file1").toString());
@ -117,7 +117,7 @@ public class ConfigurationTest extends TestCase {
}
@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));
}

View File

@ -17,7 +17,7 @@ import java.io.IOException;
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");
ToBreakRunner br = new ToBreakRunner(new File(Resources.getRoot(), "dig/hdl/negSimple.dig"));
Configuration c = Configuration.load(f)
@ -26,7 +26,7 @@ public class ReferenceTest extends TestCase {
.setCircuitProvider(br::getCircuit);
final ConfigurationTest.TestIOInterface ioInterface = new ConfigurationTest.TestIOInterface();
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("deep content", ioInterface.getFiles().get("z/test2.txt").toString());