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"> <ide name="BASYS3" clockGenerator="clockGenerator">
<commands> <commands>
<command name="Export" requires="vhdl" /> <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</arg>
<arg>vivado/&lt;?=shortname?&gt;.xpr</arg> <arg>vivado/&lt;?=shortname?&gt;.xpr</arg>
</command> </command>

View File

@ -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
*/ */

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, "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 {
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)) { @Override
out.write(content.getBytes()); public void actionPerformed(ActionEvent actionEvent) {
executeCommand(command, this);
} }
} }
@ -249,68 +232,100 @@ 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;
checkFilesToCreate(digFile, hdlModel); if (action != null)
action.setEnabled(false);
Thread t = new Thread(() -> {
try {
checkFilesToCreate(digFile, hdlModel);
String[] args = command.getArgs(); String[] args = command.getArgs();
if (args != null) { if (args != null) {
if (command.isFilter()) { if (command.isFilter()) {
final int argCount = command.getArgs().length; final int argCount = command.getArgs().length;
Context context = createContext(digFile, hdlModel); Context context = createContext(digFile, hdlModel);
for (int i = 0; i < argCount; i++) { for (int i = 0; i < argCount; i++) {
context.clearOutput(); context.clearOutput();
new Parser(args[i]).parse().execute(context); new Parser(args[i]).parse().execute(context);
args[i] = context.toString(); 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) { } catch (Exception e) {
getIoInterface().showError(command, 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() { LOGGER.info("process '" + command.getName() + "' says:\n" + consoleOut);
@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();
} }
@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));
} }
} }

View File

@ -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));
} }

View File

@ -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());