/*
* Copyright (c) 2010-2016, Sikuli.org, sikulix.com
* Released under the MIT License.
*
*/
package org.sikuli.util;
import java.io.File;
import java.io.FileOutputStream;
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.PrintStream;
import org.sikuli.basics.Debug;
import org.sikuli.basics.FileManager;
import org.sikuli.script.RunTime;
/**
* INTERNAL USE: all things needed with Linux at setup or runtime
*/
public class LinuxSupport {
static final RunTime runTime = RunTime.get();
//<editor-fold defaultstate="collapsed" desc="new logging concept">
private static final String me = "LinuxSupport: ";
private static int lvl = 3;
private static boolean isCopiedProvided = false;
private static boolean haveBuilt = false;
public static boolean shouldUseProvided = false;
// private static String osArch;
public static void log(int level, String message, Object... args) {
Debug.logx(level, me + message, args);
}
private static void logp(String message, Object... args) {
Debug.logx(-3, message, args);
}
//</editor-fold>
static File fWorkDir = null;
private static final String buildFolderSrc = "Build/Source";
private static final String buildFolderInclude = "Build/Include";
private static final String buildFolderTarget = "Build/Target";
static File fLibs = runTime.fLibsFolder;
public static final String slibVision = "VisionProxy";
public static final String libVision = "lib" + slibVision + ".so";
public static final String libGrabKey = "libJXGrabKey.so";
static boolean libSearched = false;
private static String libOpenCVcore = "";
private static String libOpenCVimgproc = "";
private static String libOpenCVhighgui = "";
private static String libTesseract = "";
private static boolean opencvAvail = true;
private static boolean tessAvail = true;
public static String getLinuxDistro() {
if (!runTime.runningLinux) {
return "";
}
String result = runTime.runcmd("lsb_release -i -r -s");
String linuxDistro = result.replaceAll("\n", " ").trim();
if (linuxDistro.contains("*** error ***")) {
log(lvl, "command returns error: lsb_release -i -r -s\n%s", result);
linuxDistro = "???DISTRO???";
}
return linuxDistro;
}
public static boolean existsLibs() {
return new File(runTime.fLibsProvided, libVision).exists() ||
new File(runTime.fLibsProvided, libGrabKey).exists();
}
public static boolean copyProvidedLibs(File fLibsFolder) {
String[] toCopy = runTime.fLibsProvided.list(new FilenameFilter() {
@Override
public boolean accept(File folder, String name) {
if (name.endsWith(".so")) {
return true;
}
return false;
}
});
boolean success = false;
if (toCopy != null) {
for (String aLib : toCopy) {
success |= FileManager.xcopy(new File(runTime.fLibsProvided, aLib),
new File(fLibsFolder, aLib));
}
}
return success;
}
public static boolean checkAllLibs() {
boolean success = false;
if (!isCopiedProvided && !runTime.useLibsProvided) {
success = true;
String[] allLibs = runTime.fLibsProvided.list(new FilenameFilter() {
@Override
public boolean accept(File folder, String name) {
if (name.toLowerCase().endsWith(".so")) {
return true;
}
return false;
}
});
if (allLibs != null) {
for (String sLib : allLibs) {
File fSrc = new File(runTime.fLibsProvided, sLib);
File fTgt = new File(runTime.fLibsFolder, sLib);
success &= FileManager.xcopy(fSrc, fTgt);
log(3, "Copy provided lib: %s (%s)", sLib, (success ? "ok" : "did not work"));
}
}
isCopiedProvided = true;
} else if (!haveBuilt) {
haveBuilt = true;
success = haveToBuild();
}
shouldUseProvided = success;
return success;
}
public static boolean haveToBuild() {
boolean success = true;
log(3, "we have to build libVisionProxy.so");
success &= checkNeeded();
if (success) {
success &= buildVision();
}
return success;
}
private static boolean checkNeeded() {
String cmdRet;
boolean checkSuccess = true;
log(lvl, "checking: availability of OpenCV and Tesseract");
log(lvl, "checking: scanning loader cache (ldconfig -p)");
cmdRet = runTime.runcmd("ldconfig -p");
if (cmdRet.contains(runTime.runCmdError)) {
log(-1, "checking: ldconfig returns error:\ns", cmdRet);
checkSuccess = false;
} else {
String[] libs = cmdRet.split("\n");
for (String libx : libs) {
libx = libx.trim();
if (!libx.startsWith("lib")) {
continue;
}
if (libx.startsWith("libopencv_core.so.")) {
libOpenCVcore = libx.split("=>")[1].trim();
} else if (libx.startsWith("libopencv_highgui.so.")) {
libOpenCVhighgui = libx.split("=>")[1].trim();
} else if (libx.startsWith("libopencv_imgproc.so.")) {
libOpenCVimgproc = libx.split("=>")[1].trim();
} else if (libx.startsWith("libtesseract.so.")) {
libTesseract = libx.split("=>")[1].trim();
}
}
if (libOpenCVcore.isEmpty() || libOpenCVhighgui.isEmpty() || libOpenCVimgproc.isEmpty()) {
log(-1, "checking: OpenCV not in loader cache (see doc-note on OpenCV)");
opencvAvail = checkSuccess = false;
} else {
log(lvl, "checking: found OpenCV libs:\n%s\n%s\n%s",
libOpenCVcore, libOpenCVhighgui, libOpenCVimgproc);
}
if (libTesseract.isEmpty()) {
log(-1, "checking: Tesseract not in loader cache (see doc-note on Tesseract)");
tessAvail = checkSuccess = false;
} else {
log(lvl, "checking: found Tesseract lib:\n%s", libTesseract);
}
}
// checking wmctrl, xdotool
// cmdRet = runTime.runcmd("wmctrl -m");
// if (cmdRet.contains(runTime.runCmdError)) {
// log(-1, "checking: wmctrl not available or not working");
// } else {
// log(lvl, "checking: wmctrl seems to be available");
// }
// cmdRet = runTime.runcmd("xdotool version");
// if (cmdRet.contains(runTime.runCmdError)) {
// log(-1, "checking: xdotool not available or not working");
// } else {
// log(lvl, "checking: xdotool seems to be available");
// }
return checkSuccess;
}
private static boolean runLdd(File lib) {
// ldd -r lib
// undefined symbol: _ZN2cv3MatC1ERKS0_RKNS_5Rect_IiEE (./libVisionProxy.so)
String cmdRet = runTime.runcmd("ldd -r " + lib);
String[] retLines;
boolean success = true;
retLines = cmdRet.split("continued: build on the fly on Linux at runtime: if bundled do not work, looking for provided - if these do not work, try to build. setup not ready yet. \n");
String libName = lib.getName();
String libsMissing = "";
for (String line : retLines) {
if (line.contains("undefined symbol:") && line.contains(libName)) {
line = line.split("symbol:")[1].trim().split("\\s")[0];
libsMissing += line + ":";
}
}
if (libsMissing.isEmpty()) {
log(lvl, "checking: should work: %s", libName);
} else {
log(-1, "checking: might not work, has undefined symbols: %s", libName);
log(lvl, "%s", libsMissing);
success = false;
}
return success;
}
public static boolean buildVision() {
File fLibsSaveDir = runTime.fLibsProvided;
fWorkDir = fLibsSaveDir.getParentFile();
fWorkDir.mkdirs();
fLibsSaveDir.mkdir();
File fTarget = new File(fWorkDir, buildFolderTarget);
File fSource = new File(fWorkDir, buildFolderSrc);
File fInclude = new File(fWorkDir, buildFolderInclude);
fInclude.mkdirs();
File[] javas = new File[]{null, null};
javas[0] = new File(System.getProperty("java.home"));
String jhome = System.getenv("JAVA_HOME");
if (jhome != null) {
javas[1] = new File(jhome);
}
log(lvl, "buildVision: starting inline build: libVisionProxy.so");
log(lvl, "buildVision: java.home from java props: %s", javas[0]);
log(lvl, "buildVision: JAVA_HOME from environment: %s", javas[1]);
File javaHome = null;
for (File jh : javas) {
if (jh == null) {
continue;
}
if (!new File(jh, "bin/javac").exists()) {
jh = jh.getParentFile();
}
if (!new File(jh, "bin/javac").exists()) {
jh = null;
}
if (jh != null) {
if (new File(jh, "include/jni.h").exists()) {
javaHome = jh;
break;
}
}
}
if (javaHome == null) {
log(-1, "buildVision: no valid Java JDK available nor found");
return false;
}
log(lvl, "buildVision: JDK: found at: %s", javaHome);
File cmdFile = new File(fWorkDir, "runBuild");
String libVisionPath = new File(fTarget, libVision).getAbsolutePath();
String sRunBuild = runTime.extractResourceToString("/Support/Linux", "runBuild", "");
sRunBuild = sRunBuild.replace("#jdkdir#", "jdkdir=" + javaHome.getAbsolutePath());
String inclUsr = "/usr/include";
String inclUsrLocal = "/usr/local/include";
boolean exportIncludeOpenCV = false;
boolean exportIncludeTesseract = false;
String inclLib = "opencv2";
if (!new File(inclUsr, inclLib).exists() && !new File(inclUsrLocal, inclLib).exists()) {
log(lvl, "buildVision: opencv-include: not found - using the bundled include files");
exportIncludeOpenCV = true;
}
inclLib = "tesseract";
if (!new File(inclUsr, inclLib).exists() && !new File(inclUsrLocal, inclLib).exists()) {
log(lvl, "buildVision: tesseract-include: not found - using the bundled include files");
exportIncludeTesseract = true;
}
boolean success = (null != runTime.extractResourcesToFolder("/srcnativelibs/Vision", fSource, null));
if (!success) {
log(-1, "buildVision: cannot export bundled sources");
}
if (exportIncludeOpenCV) {
if (null == runTime.extractResourcesToFolder("/srcnativelibs/Include/OpenCV", fInclude, null)) {
log(-1, "buildVision: cannot export opencv includes");
success = false;
}
}
if (exportIncludeTesseract) {
if (null == runTime.extractResourcesToFolder("/srcnativelibs/Include/Tesseract", fInclude, null)) {
log(-1, "buildVision: cannot export tesseract includes");
success = false;
}
}
if (success && (exportIncludeOpenCV || exportIncludeTesseract)) {
sRunBuild = sRunBuild.replace("#extrainclude#", "extrainclude=$work/Include");
}
if (success) {
sRunBuild = sRunBuild.replace("#work#", "work=" + fTarget.getParentFile().getAbsolutePath());
sRunBuild = sRunBuild.replace("#opencvcore#", "opencvcore=" + libOpenCVcore);
sRunBuild = sRunBuild.replace("#opencvimgproc#", "opencvimgproc=" + libOpenCVimgproc);
sRunBuild = sRunBuild.replace("#opencvhighgui#", "opencvhighgui=" + libOpenCVhighgui);
sRunBuild = sRunBuild.replace("#tesseractlib#", "tesseractlib=" + libTesseract);
}
log(lvl, "**** content of build script:\n(stored at: %s)\n%s\n**** content end",
cmdFile.getAbsolutePath(), sRunBuild);
try {
PrintStream psCmdFile = new PrintStream(cmdFile);
psCmdFile.print(sRunBuild);
psCmdFile.close();
} catch (Exception ex) {
log(-1, "buildVision: problem writing command file:\n%s", cmdFile);
return false;
}
cmdFile.setExecutable(true);
if (success && opencvAvail && tessAvail) {
log(lvl, "buildVision: running build script");
String cmdRet = runTime.runcmd(cmdFile.getAbsolutePath());
if (cmdRet.contains(runTime.runCmdError)) {
log(-1, "buildVision: build script returns error:\n%s", cmdRet);
return false;
} else {
log(lvl, "buildVision: checking created libVisionProxy.so");
if (!runLdd(new File(libVisionPath))) {
log(-1, "------- output of the build run\n%s", cmdRet);
return false;
}
}
} else {
log(-1, "buildVision: corrrect the reported problems and try again");
return false;
}
File providedLib = new File(fLibsSaveDir, libVision);
if (!FileManager.xcopy(new File(libVisionPath), providedLib)) {
log(-1, "buildVision: could not save:\n%s", providedLib);
return false;
}
if (runTime.fLibsFolder.exists()) {
copyProvidedLibs(runTime.fLibsFolder);
}
log(lvl, "buildVision: ending inline build: success:\n%s", providedLib);
return true;
}
//TODO needed??? processLibs
//<editor-fold defaultstate="collapsed" desc="obsolete???">
private static final String[] libsExport = new String[]{null, null};
private static final String[] libsCheck = new String[]{null, null};
private static boolean processLibs(String fpLibsJar) {
// boolean shouldBuildVisionNow = false;
// if (!fLibs.exists()) {
// fLibs.mkdirs();
// }
// if (fLibs.exists()) {
// if (fpLibsJar != null) {
// runTime.extractResourcesToFolderFromJar(fpLibsJar, runTime.fpJarLibs, fLibs, null);
// } else {
// runTime.extractResourcesToFolder(runTime.fpJarLibs, fWorkDir, null);
// }
// libsCheck[0] = new File(fLibs, libVision).getAbsolutePath();
// libsCheck[1] = new File(fLibs, libGrabKey).getAbsolutePath();
// File fLibCheck;
// for (int i = 0; i < libsCheck.length; i++) {
// fLibCheck = new File(libsCheck[i]);
// if (fLibCheck.exists()) {
// if (!checklibs(fLibCheck)) {
////TODO why? JXGrabKey unresolved: pthread
// if (i == 0) {
// if (libsExport[i] == null) {
// log(-1, "provided %s might not be useable on this Linux - see log", fLibCheck.getName());
// } else {
// log(-1, "bundled %s might not be useable on this Linux - see log", fLibCheck.getName());
// }
// shouldBuildVisionNow = true;
// }
// }
// } else {
// log(-1, "check not possible for\n%s", fLibCheck);
// }
// }
// } else {
// log(-1, "check useability of libs: problems with libs folder\n%s", fLibs);
// }
// return shouldBuildVisionNow;
return true;
}
private static boolean checklibs(File lib) {
String cmdRet;
String[] retLines;
boolean checkSuccess = true;
if (!libSearched) {
checkSuccess = checkNeeded();
libSearched = true;
}
log(lvl, "checking\n%s", lib);
// readelf -d lib
// 0x0000000000000001 (NEEDED) Shared library: [libtesseract.so.3]
cmdRet = runTime.runcmd("readelf -d " + lib);
if (cmdRet.contains(runTime.runCmdError)) {
log(-1, "checking: readelf returns error:\ns", cmdRet);
checkSuccess = false;
} else {
retLines = cmdRet.split("\n");
String libsNeeded = "";
for (String line : retLines) {
if (line.contains("(NEEDED)")) {
line = line.split("\\[")[1].replace("]", "");
libsNeeded += line + ":";
}
}
log(lvl, libsNeeded);
}
if (!runLdd(lib)) {
checkSuccess = false;
}
// return false; // for testing
return checkSuccess;
}
//</editor-fold>
}