mirror of
https://github.com/hneemann/Digital.git
synced 2025-09-13 23:06:22 -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">
|
||||
<commands>
|
||||
<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/<?=shortname?>.xpr</arg>
|
||||
</command>
|
||||
|
@ -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
|
||||
*/
|
||||
|
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, "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));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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));
|
||||
}
|
||||
|
||||
|
@ -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());
|
||||
|
Loading…
x
Reference in New Issue
Block a user