tests: Don't rely on build directory layout

Rather than having a hardcoded dependency on the build-directory layout,
use an explicit module-map environment variable, which rewrites requests
for modules and helper/libexec binaries to specific paths.

Pekka: This will help with migration to Meson where setting up the paths
according to autotools would be painful and unnecessary.

Emre: This should also help setting up the test suite after a
cross-compile.

Pekka: A caveat here is that this patch makes it slightly easier to load
external backends by abusing the module map. External backends are
specifically not supported in libweston.

Signed-off-by: Daniel Stone <daniels@collabora.com>

v2:

Fixed ivi_layout-test-plugin.c:wet_module_init().
Do not change the lookup name of ivi-layout.ivi.

Improved documentation of weston_module_path_from_env() and made it cope
with map strings that a) do not end with a semicolon, and b) have
multiple consecutive semicolons.

Let WESTON_MODULE_MAP be printed into the test log so that it is easier
to run tests manually.

Reviewed-by: Daniel Stone <daniels@collabora.com>
Reviewed-by: Emre Ucan <eucan@de.adit-jv.com>

Suggested by Emil: Use a variable for strlen(name).

Signed-off-by: Pekka Paalanen <pekka.paalanen@collabora.co.uk>
Reviewed-by: Emil Velikov <emil.velikov@collabora.com>
diff --git a/libweston/compositor.c b/libweston/compositor.c
index 91f311d..8e01eac 100644
--- a/libweston/compositor.c
+++ b/libweston/compositor.c
@@ -6573,10 +6573,60 @@
 	*micro = WESTON_VERSION_MICRO;
 }
 
+/**
+ * Attempts to find a module path from the module map specified in the
+ * environment. If found, writes the full path into the path variable.
+ *
+ * The module map is a string in environment variable WESTON_MODULE_MAP, where
+ * each entry is of the form "name=path" and entries are separated by
+ * semicolons. Whitespace is significant.
+ *
+ * \param name The name to search for.
+ * \param path Where the path is written to if found.
+ * \param path_len Allocated bytes at \c path .
+ * \returns The length of the string written to path on success, or 0 if the
+ * module was not specified in the environment map or path_len was too small.
+ */
+WL_EXPORT size_t
+weston_module_path_from_env(const char *name, char *path, size_t path_len)
+{
+	const char *mapping = getenv("WESTON_MODULE_MAP");
+	const char *end;
+	const int name_len = strlen(name);
+
+	if (!mapping)
+		return 0;
+
+	end = mapping + strlen(mapping);
+	while (mapping < end && *mapping) {
+		const char *filename, *next;
+
+		/* early out: impossibly short string */
+		if (end - mapping < name_len + 1)
+			return 0;
+
+		filename = &mapping[name_len + 1];
+		next = strchrnul(mapping, ';');
+
+		if (strncmp(mapping, name, name_len) == 0 &&
+		    mapping[name_len] == '=') {
+			size_t file_len = next - filename; /* no trailing NUL */
+			if (file_len >= path_len)
+				return 0;
+			strncpy(path, filename, file_len);
+			path[file_len] = '\0';
+			return file_len;
+		}
+
+		mapping = next + 1;
+	}
+
+	return 0;
+}
+
 WL_EXPORT void *
 weston_load_module(const char *name, const char *entrypoint)
 {
-	const char *builddir = getenv("WESTON_BUILD_DIR");
 	char path[PATH_MAX];
 	void *module, *init;
 	size_t len;
@@ -6585,10 +6635,8 @@
 		return NULL;
 
 	if (name[0] != '/') {
-		if (builddir)
-			len = snprintf(path, sizeof path, "%s/.libs/%s",
-				       builddir, name);
-		else
+		len = weston_module_path_from_env(name, path, sizeof path);
+		if (len == 0)
 			len = snprintf(path, sizeof path, "%s/%s",
 				       LIBWESTON_MODULEDIR, name);
 	} else {