Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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
4 changes: 4 additions & 0 deletions docs/charts/keyboards/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@ Run this from the command line in the top level directory:
- Change the **goal** to `integration-test`
- Choose Run

## To run from `GenerateKeyboardCharts`

If you either have Node.js isntalled, or you've run a CLDR maven build (including cldr-apps), then calling `GenerateKeyboardCharts` (or `GenerateAllCharts`) ought to attempt to build.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note: cldr-apps do not build in Maven for me, nor can I update Maven for them.

Updating:

Could not update project cldr-apps configuration
Cannot invoke "org.eclipse.wst.common.componentcore.internal.WorkbenchComponent.findResourcesByRuntimePath(org.eclipse.core.runtime.IPath)" because "aComponent" is null

Building:
53 Error messages.

Description	Resource	Path	Location	Type
com.ibm.icu.dev cannot be resolved	tc-mzfix.jsp	/cldr-apps/src/main/webapp	line 122	JSP Problem
ElapsedTimer cannot be resolved to a type	admin-DAIPVotes.jsp	/cldr-apps/src/main/webapp	line 31	JSP Problem
ElapsedTimer cannot be resolved to a type	admin-DAIPVotes.jsp	/cldr-apps/src/main/webapp	line 31	JSP Problem
ElapsedTimer cannot be resolved to a type	admin-softToHardVotes.jsp	/cldr-apps/src/main/webapp	line 31	JSP Problem
ElapsedTimer cannot be resolved to a type	admin-softToHardVotes.jsp	/cldr-apps/src/main/webapp	line 31	JSP Problem
ElapsedTimer cannot be resolved to a type	browse_results.jsp	/cldr-apps/src/main/webapp	line 17	JSP Problem
ElapsedTimer cannot be resolved to a type	browse_results.jsp	/cldr-apps/src/main/webapp	line 17	JSP Problem
ElapsedTimer cannot be resolved to a type	xpath_results.jsp	/cldr-apps/src/main/webapp	line 25	JSP Problem
ElapsedTimer cannot be resolved to a type	xpath_results.jsp	/cldr-apps/src/main/webapp	line 25	JSP Problem
JSONArray cannot be resolved to a type	AdminAjax.jsp	/cldr-apps/src/main/webapp	line 62	JSP Problem
…
The method errln(String) from the type SurveyLog is not visible	st_footer.jsp	/cldr-apps/src/main/webapp/WEB-INF/tmpl	line 45	JSP Problem
The method getDefaultUrl() is undefined for the type SubtypeToURLMap	tc-all-errors.jsp	/cldr-apps/src/main/webapp	line 68	JSP Problem
The method getGuestsAndUsers() is undefined for the type SurveyMain	st_footer.jsp	/cldr-apps/src/main/webapp/WEB-INF/tmpl	line 15	JSP Problem
The method reload() from the type SubtypeToURLMap is not visible	tc-all-errors.jsp	/cldr-apps/src/main/webapp	line 22	JSP Problem

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But a further question: why can't the Keyboard charts be built in the same way other charts are? Is there something special about keyboards that are different?

@srl295 srl295 May 20, 2026

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I couldn't find a Java version of the BNF processor.

Sorry that was for the unit tests. It could be rewritten, it's in JavaScript because it's integrated into the chart page.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note: cldr-apps do not build in Maven for me, nor can I update Maven for them.

This is maybe worth a separate issue.

Updating:

Could not update project cldr-apps configuration
Cannot invoke "org.eclipse.wst.common.componentcore.internal.WorkbenchComponent.findResourcesByRuntimePath(org.eclipse.core.runtime.IPath)" because "aComponent" is null

That isn't from maven.

Building:
53 Error messages.

Description	Resource	Path	Location	Type
com.ibm.icu.dev cannot be resolved	tc-mzfix.jsp	/cldr-apps/src/main/webapp	line 122	JSP Problem
ElapsedTimer cannot be resolved to a type	admin-DAIPVotes.jsp	/cldr-apps/src/main/webapp	line 31	JSP Problem
ElapsedTimer cannot be resolved to a type	admin-DAIPVotes.jsp	/cldr-apps/src/main/webapp	line 31	JSP Problem
ElapsedTimer cannot be resolved to a type	admin-softToHardVotes.jsp	/cldr-apps/src/main/webapp	line 31	JSP Problem
ElapsedTimer cannot be resolved to a type	admin-softToHardVotes.jsp	/cldr-apps/src/main/webapp	line 31	JSP Problem
ElapsedTimer cannot be resolved to a type	browse_results.jsp	/cldr-apps/src/main/webapp	line 17	JSP Problem
ElapsedTimer cannot be resolved to a type	browse_results.jsp	/cldr-apps/src/main/webapp	line 17	JSP Problem
ElapsedTimer cannot be resolved to a type	xpath_results.jsp	/cldr-apps/src/main/webapp	line 25	JSP Problem
ElapsedTimer cannot be resolved to a type	xpath_results.jsp	/cldr-apps/src/main/webapp	line 25	JSP Problem
JSONArray cannot be resolved to a type	AdminAjax.jsp	/cldr-apps/src/main/webapp	line 62	JSP Problem
…
The method errln(String) from the type SurveyLog is not visible	st_footer.jsp	/cldr-apps/src/main/webapp/WEB-INF/tmpl	line 45	JSP Problem
The method getDefaultUrl() is undefined for the type SubtypeToURLMap	tc-all-errors.jsp	/cldr-apps/src/main/webapp	line 68	JSP Problem
The method getGuestsAndUsers() is undefined for the type SurveyMain	st_footer.jsp	/cldr-apps/src/main/webapp/WEB-INF/tmpl	line 15	JSP Problem
The method reload() from the type SubtypeToURLMap is not visible	tc-all-errors.jsp	/cldr-apps/src/main/webapp	line 22	JSP Problem

These are problems but shouldn't prevent a maven build.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I had no problems building in eclipse. Right-clicked on tools and chose "Maven Build…", setting the goal to "compile". It executed all of the steps appropriately.


## Trying them out

- `npm run serve` will serve the charts locally on <http://localhost:3000>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,19 @@

import java.io.File;
import java.io.IOException;
import java.lang.ProcessBuilder.Redirect;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardCopyOption;
import java.util.concurrent.TimeUnit;
import org.unicode.cldr.util.CLDRPaths;

public class GenerateKeyboardCharts {

static final String SUBDIR = "keyboards";
private static final String PATH_TO_SUBDIR = "docs/charts/" + SUBDIR;
// subdir from CLDR_DIR to a possible node install
private static final String NODE_SUBDIR = "tools/cldr-apps/js/node";
static IOException copyErr = null;

public static void main(String args[]) throws IOException {
Expand All @@ -20,20 +25,41 @@ public static void main(String args[]) throws IOException {
if (!mainDir.isDirectory()) {
throw new IOException("Main dir doesn't exist: " + mainDir);
}
final File kbdDir = new File(CLDRPaths.BASE_DIRECTORY, "docs/charts/" + SUBDIR);
final File kbdDir = new File(CLDRPaths.BASE_DIRECTORY, PATH_TO_SUBDIR);
if (!kbdDir.exists()) {
throw new IOException("Keyboards root dir doesn't exist: " + kbdDir);
}
final File kbdStatic = new File(kbdDir, "static");
final File kbdStaticData = new File(kbdDir, "static/data");
if (kbdStaticData.exists()) {
System.out.println("Using existing Keyboard data " + kbdStaticData.getPath());
} else {
// attempt to build data
System.out.println("Attempting to generate: " + kbdStaticData.getPath());
try {
final File nodeSubDir = new File(CLDRPaths.BASE_DIRECTORY, NODE_SUBDIR);
if (nodeSubDir.isDirectory() && tryNodeInstall(nodeSubDir, kbdDir, kbdStaticData)) {
//
} else {
tryNodeInstall(null, kbdDir, kbdStaticData);
}
} catch (Throwable t) {
t.printStackTrace();
System.err.println(
"## Error, was not able to automatically build keyboard charts.");
}
}

// At this point, we should already have the data
if (!kbdStaticData.exists()) {
System.err.println(
"ERROR: " + kbdStaticData + " does not exist. Keyboard charts weren't run.");
System.err.println("See " + new File(kbdDir, "README.md") + " for help.");
return;
}
final File staticTarg = new File(mainDir, SUBDIR + "/static");
final File staticDataTarg = new File(mainDir, SUBDIR + "/static/data");
final File mainChartDir = new File(mainDir, SUBDIR);
final File staticTarg = new File(mainChartDir, "static");
final File staticDataTarg = new File(staticTarg, "data");
if (staticDataTarg.mkdirs()) {
System.err.println("Created: " + staticDataTarg);
}
Expand Down Expand Up @@ -70,5 +96,79 @@ public static void main(String args[]) throws IOException {
});
// rethrow any error
if (copyErr != null) throw copyErr;
System.out.println();
System.out.println(
"🖮 Success!\n🖮 Keyboard Charts updated in "
+ kbdDir
+ "\n🖮 and copied to "
+ mainChartDir);
}

private static final boolean ON_WINDOWS =
System.getProperty("os.name", "unknown").contains("Windows");

private static boolean tryNodeInstall(File nodeSubDir, File kbdDir, File staticDir) {
try {
String npmName = ON_WINDOWS ? "npm.cmd" : "npm";
if (nodeSubDir != null) {
File npmExe = new File(nodeSubDir, npmName);
if (npmExe.canExecute()) {
npmName = npmExe.getAbsolutePath();
}
}
String nodeName = ON_WINDOWS ? "node.exe" : "node";
if (nodeSubDir != null) {
File nodeExe = new File(nodeSubDir, nodeName);
if (nodeExe.canExecute()) {
nodeName = nodeExe.getAbsolutePath();
}
}
if (ON_WINDOWS) {
// on windows, we need to run npm in two steps
final String cmd1[] = {npmName, "install", "--ignore-scripts=true"};
final String cmd2[] = {nodeName, "build.mjs"};
return tryNodeCommand(kbdDir, staticDir, cmd1)
&& tryNodeCommand(kbdDir, staticDir, cmd2)
&& staticDir.isDirectory();
} else {
final String cmd[] = {npmName, "install"};
return tryNodeCommand(kbdDir, staticDir, cmd) && staticDir.isDirectory();
}
} catch (IOException | InterruptedException e) {
e.printStackTrace();
System.err.println("Failed to node install from " + nodeSubDir);
return false;
} finally {
System.out.println("------ Done executing in " + nodeSubDir);
}
}

private static boolean tryNodeCommand(File kbdDir, File staticDir, final String[] cmd)
throws IOException, InterruptedException {
final int timeoutSeconds = 120;
System.out.println(
"# Attempting: "
+ String.join(" ", cmd)
+ "\n# (timeout: "
+ timeoutSeconds
+ " seconds)");
final Process p =
new ProcessBuilder(cmd)
.directory(kbdDir)
.redirectError(Redirect.INHERIT)
.redirectOutput(Redirect.INHERIT)
.start();
p.waitFor(timeoutSeconds, TimeUnit.SECONDS);
if (p.isAlive()) {
System.err.println(".. stuck");
p.destroyForcibly();
return false;
}
if (p.exitValue() != 0) {
System.err.println(".. failed: " + p.exitValue());

return false;
}
return true;
}
}
Loading