diff --git a/CHANGELOG.md b/CHANGELOG.md
index 3f928957c149b43f0ad48e8f52d09c717a23e28c..90216055d4c9ef81c7e4de4f635af94a7e7399d6 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -6,6 +6,7 @@
 * Added descriptions for more targets when running `make help`
 * Added a new test target for individual modules, available as `make test`
 * Added script in `tools/test_installed_modules.sh` that tests all modules in an installation
+* Build numbers are now prefixed with a `+`.
 
 ## Bugfixes
 
@@ -18,6 +19,8 @@
 * Removed all references to `sudo`. If `sudo` needs to be used, it should be done manually.
 * Fixed issue with module name checking which caused modules to fail to build on certain linux distributions
 * Exact module version is fetched correctly at build-time to ensure that new releases do not affect upstream dependencies
+* Issue involving priority between some test versions fixed
+* `make patch` when there are no patch files will no longer return an error code.
 
 ## Other changes
 * Removed `require` as a submodule and merged it into `e3-require`.
diff --git a/configure/modules/CONFIG b/configure/modules/CONFIG
index f9893beef0f96ea7bdd21c75bb8b926e66ee04f6..31571ac1977b307e3731bd2bf6c87275de3df328 100644
--- a/configure/modules/CONFIG
+++ b/configure/modules/CONFIG
@@ -1,6 +1,7 @@
 # -*- mode: Makefile;-*-
 
-E3_MODULE_VERSION:=$(E3_MODULE_VERSION)$(shell [[ "$(E3_MODULE_VERSION)" =~ ^[0-9]+\.[0-9]+\.[0-9]+$$ ]] && echo -0)
+# Update the module version (for numeric versions) with a build number of 0 if none is specified
+E3_MODULE_VERSION:=$(E3_MODULE_VERSION)$(shell [[ "$(E3_MODULE_VERSION)" =~ ^[0-9]+\.[0-9]+\.[0-9]+$$ ]] && echo +0)
 
 include $(EPICS_BASE)/configure/CONFIG_BASE_VERSION
 include $(REQUIRE_CONFIG)/CONFIG_REQUIRE
diff --git a/configure/modules/DEFINES_FT b/configure/modules/DEFINES_FT
index e81b52d948bb062094e61ed6e39bbe112097c46c..c3337cb43552e2da41edfa0abedb5bfbae76dbf7 100644
--- a/configure/modules/DEFINES_FT
+++ b/configure/modules/DEFINES_FT
@@ -16,19 +16,28 @@ git submodule update --remote --merge $@/
 endef
 
 define patch_site
-for i in $(shell ls $(TOP)/patch/Site/$(E3_MODULE_VERSION)-+([a-zA-Z])*.p0.patch); do\
-	printf "\nPatching %s with the file : %s\n" "$(E3_MODULE_SRC_PATH)" "$$i"; \
-	git apply --directory=$(E3_MODULE_SRC_PATH) --ignore-whitespace -p0 $$i;\
-done
+patches=$$(ls $(TOP)/patch/Site/$(E3_MODULE_VERSION)-*.p0.patch 2> /dev/null); \
+if [ -n "$$patches" ]; then \
+  for i in $$patches; do \
+    printf "\nPatching %s with the file : %s\n" "$(E3_MODULE_SRC_PATH)" "$$i"; \
+    git apply --directory=$(E3_MODULE_SRC_PATH) --ignore-whitespace -p0 $$i; \
+  done; \
+else \
+  echo ">>> No patches to apply" >&2; \
+fi
 endef
 
 
 define patch_revert_site
-for i in $(shell ls $(TOP)/patch/Site/$(E3_MODULE_VERSION)-+([a-zA-Z])*.p0.patch); do\
-	printf "\nReverting applied patch %s with the file : %s\n" "$(E3_MODULE_SRC_PATH)" "$$i"; \
-	git apply -R --directory=$(E3_MODULE_SRC_PATH) --ignore-whitespace -p0 $$i;\
-done
-
+patches=$$(ls $(TOP)/patch/Site/$(E3_MODULE_VERSION)-*.p0.patch 2> /dev/null); \
+if [ -n "$$patches" ]; then \
+  for i in $$patches; do\
+    printf "\nReverting applied patch %s with the file : %s\n" "$(E3_MODULE_SRC_PATH)" "$$i"; \
+    git apply -R --directory=$(E3_MODULE_SRC_PATH) --ignore-whitespace -p0 $$i;\
+  done \
+else \
+  echo ">>> No patches to revert" >&2; \
+fi
 endef
 
 
diff --git a/require-ess/src/require.c b/require-ess/src/require.c
index 43ba75a78a431d4c04319750d5e843e2ec5a60db..bda302bc48d8a0a32679ccd2254c209bda9877b9 100644
--- a/require-ess/src/require.c
+++ b/require-ess/src/require.c
@@ -38,6 +38,7 @@ epicsShareFunc int epicsShareAPI iocshCmd(const char *cmd);
 #include <osiFileName.h>
 #include <epicsExport.h>
 
+#include "version.h"
 #include "require.h"
 
 int requireDebug;
@@ -805,7 +806,7 @@ int libversionShow(const char *outfile)
     if (requireDebug) \
     printf(__VA_ARGS__)
 
-static int compareDigit(int found, int requested, char *name)
+static int compareDigit(int found, int requested, const char *name)
 {
     debug("require: compareDigit: found %d, requested %d for digit %s\n", found, requested, name);
     if (found < requested)
@@ -822,17 +823,50 @@ static int compareDigit(int found, int requested, char *name)
     return MATCH;
 }
 
+static int compareNumericVersion(semver_t *sv_found, semver_t *sv_request, int already_matched)
+{
+    int match;
+
+    match = compareDigit(sv_found->major, sv_request->major, "major");
+    if (match != MATCH)
+    {
+        return match;
+    }
+    match = compareDigit(sv_found->minor, sv_request->minor, "minor");
+    if (match != MATCH)
+    {
+        return match;
+    }
+    match = compareDigit(sv_found->patch, sv_request->patch, "patch");
+    if (match != MATCH)
+    {
+        return match;
+    }
+    
+    if (sv_request->build == -1)
+    {
+        if (already_matched)
+        {
+            debug("require: compareVersions: No build number requested. Returning HIGHER\n");
+            return HIGHER;
+        }
+        else
+        {
+            debug("require: compareVersions: No build number requested. Returning MATCH\n");
+            return MATCH;
+        }
+    } 
+    return compareDigit(sv_found->build, sv_request->build, "build");
+}
+
 /*
  * Returns if the version <found> is higher than <request>.
  */
 static int compareVersions(const char *found, const char *request, int already_matched)
 {
-    int found_major = 0, found_minor = 0, found_patch = 0, found_parts = 0, found_build = 0;
-    int req_major = 0, req_minor = 0, req_patch = 0, req_parts = 0, req_build = 0;
+    semver_t *sv_found, *sv_request;
     int match;
 
-    const char *version_string = "%d.%d.%d-%d";
-
     debug("require: compareVersions(found=%s, request=%s)\n", found, request);
 
     if (request == NULL || request[0] == 0)
@@ -846,60 +880,43 @@ static int compareVersions(const char *found, const char *request, int already_m
         return MISMATCH;
     }
 
-    found_parts = sscanf(found, version_string, &found_major, &found_minor, &found_patch, &found_build);
+    sv_found = (semver_t *)calloc(1, sizeof(semver_t));
+    sv_request = (semver_t *)calloc(1, sizeof(semver_t));
 
-    req_parts = sscanf(request, version_string, &req_major, &req_minor, &req_patch, &req_build);
+    parse_semver(found, sv_found);
+    parse_semver(request, sv_request);
 
     // test version, look for exact.
-    if (req_parts == 0 || req_parts == 1 || req_parts == 2)
+    if (strlen(sv_request->test_str) > 0)
     {
         if (strcmp(found, request) == 0)
         {
             debug("require: compareVersions: Test version requested and found, matches exactly\n");
-            return EXACT;
+            match = EXACT;
         }
-
-        if (found_parts == 0 || found_parts == 1 || found_parts == 2)
+        else if (strlen(sv_found->test_str) > 0)
         {
             debug("require: compareVersions: Test versions requested and found, no match\n");
-            return MISMATCH;
+            match = MISMATCH;
+        } 
+        else
+        {
+            debug("require: compareVersions: found numeric version, higher than test\n");
+            match = HIGHER;
         }
-
-        debug("require: compareVersions: found numeric version, higher than test\n");
-        return HIGHER;
-    }
-
-    if (found_parts == 0 || found_parts == 1 || found_parts == 2)
+    } 
+    else if (strlen(sv_found->test_str) > 0)
     {
         debug("require: compareVersions: Numeric version requested, test version found\n");
-        return MISMATCH;
+        match = MISMATCH;
     }
-
-    // At least three digits specifed
-    match = compareDigit(found_major, req_major, "major");
-    if (match != MATCH)
-        return match;
-    match = compareDigit(found_minor, req_minor, "minor");
-    if (match != MATCH)
-        return match;
-    match = compareDigit(found_patch, req_patch, "patch");
-    if (match != MATCH)
-        return match;
-
-    if (req_parts == 3)
+    else 
     {
-        if (already_matched)
-        {
-            debug("require: compareVersions: No build number requested. Returning HIGHER\n");
-            return HIGHER;
-        }
-        else
-        {
-            debug("require: compareVersions: No build number requested. Returning MATCH\n");
-            return MATCH;
-        }
+        match = compareNumericVersion(sv_found, sv_request, already_matched);
     }
-    return compareDigit(found_build, req_build, "build");
+    cleanup_semver(sv_found);
+    cleanup_semver(sv_request);
+    return match;
 }
 
 /* require (module)
diff --git a/require-ess/src/version.c b/require-ess/src/version.c
new file mode 100644
index 0000000000000000000000000000000000000000..9d716c535f995af0671ff53b804421c39570850a
--- /dev/null
+++ b/require-ess/src/version.c
@@ -0,0 +1,72 @@
+#include <regex.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include "version.h"
+
+void cleanup_semver(semver_t *s)
+{
+    free(s->version_str);
+    free(s);
+}
+
+static void init_semver(const char *version, semver_t *s)
+{
+    s->version_str = calloc(strlen(version) + 1, sizeof(char));
+    strcpy(s->version_str, version);
+    s->test_str = "";
+
+    s->major = 0;
+    s->minor = 0;
+    s->patch = 0;
+    s->build = -1;
+}
+
+static void fetch_part(char *version_str, const regmatch_t *groups, unsigned int ix, int *part)
+{
+    char tmp;
+
+    tmp = version_str[groups[ix].rm_eo];
+    version_str[groups[ix].rm_eo] = 0;
+    *part = atoi(version_str + groups[ix].rm_so);
+    version_str[groups[ix].rm_eo] = tmp;
+}
+
+int parse_semver(const char *version, semver_t *s)
+{
+    static const char* version_regex = "^(([0-9]+)\\.([0-9]+)\\.([0-9]+)(\\+([0-9]+))?)?(.*)$";
+    static const unsigned int max_regex_groups = 7 + 1;
+    static const unsigned int major_ix = 2;
+    static const unsigned int minor_ix = 3;
+    static const unsigned int patch_ix = 4;
+    static const unsigned int build_ix = 6;
+    static const unsigned int test_ix = 7;
+
+    regex_t compiled;
+    regmatch_t groups[max_regex_groups];
+
+    init_semver(version, s);
+
+    if (s->version_str == NULL || s->version_str[0] == 0)
+    {
+        return 1;
+    }
+
+    if (regcomp(&compiled, version_regex, REG_EXTENDED))
+    {
+        return 1;
+    }
+
+    if (regexec(&compiled, s->version_str, max_regex_groups, groups, 0) == 0)
+    {
+        fetch_part(s->version_str, groups, major_ix, &s->major);
+        fetch_part(s->version_str, groups, minor_ix, &s->minor);
+        fetch_part(s->version_str, groups, patch_ix, &s->patch);
+        if (groups[build_ix].rm_so != (regoff_t)-1)
+        {
+            fetch_part(s->version_str, groups, build_ix, &s->build);
+        }
+        s->test_str = s->version_str + groups[test_ix].rm_so;
+    }
+    return 0;
+}
diff --git a/require-ess/src/version.h b/require-ess/src/version.h
new file mode 100644
index 0000000000000000000000000000000000000000..f404c8de797ac7181b8d4707c77bccc516c2b279
--- /dev/null
+++ b/require-ess/src/version.h
@@ -0,0 +1,12 @@
+typedef struct semver_t {
+    char *version_str;
+    int major;
+    int minor;
+    int patch;
+    int build; // can be negative; implies that build has not been specified
+    char *test_str;
+} semver_t;
+
+void cleanup_semver(semver_t *s);
+
+int parse_semver(const char *version, semver_t *s);
\ No newline at end of file
diff --git a/require-ess/tools/build_number.sh b/require-ess/tools/build_number.sh
index bfef0766061bfd860e35ca72ef82a281c9b77fa0..077cd15183ff41f96c1bcc6f71cac5bfb5a96cd5 100755
--- a/require-ess/tools/build_number.sh
+++ b/require-ess/tools/build_number.sh
@@ -16,7 +16,7 @@ v="$3"
 
 if [[ $v =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
   # shellcheck disable=SC2012
-  basename "$(ls -dvr "$p/$m/$v"?(-+([0-9])) 2>/dev/null | head -n 1)"
+  basename "$(ls -dvr "$p/$m/$v"?(++([0-9])) 2>/dev/null | head -n 1)"
 else
   echo "$v"
 fi
\ No newline at end of file
diff --git a/require-ess/tools/driver.makefile b/require-ess/tools/driver.makefile
index 967314d2b0ea008abddbcdb33c56d5a0610b75cc..df17899cad81d8347748d55fda99ee8fb173308a 100644
--- a/require-ess/tools/driver.makefile
+++ b/require-ess/tools/driver.makefile
@@ -101,8 +101,8 @@ RM = rm -f
 CP = cp
 
 # This is to allow for build numbers in recognized versions. First regex is for grep, second for sed.
-VERSIONGLOB = +([0-9]).+([0-9]).+([0-9])?(-+([0-9]))
-VERSIONREGEX = [0-9]+\.[0-9]+\.[0-9]+(-[0-9]+)?
+VERSIONGLOB = +([0-9]).+([0-9]).+([0-9])?(++([0-9]))
+VERSIONREGEX = [0-9]+\.[0-9]+\.[0-9]+(\+[0-9]+)?
 
 # Some generated file names:
 VERSIONFILE = ${PRJ}_version_${LIBVERSION}.c
diff --git a/require.Makefile b/require.Makefile
index a1acaf6e63f00b635920647bd1ccdf503b9ff172..d07ccfc6f8ede6bcd2435cae8bbb4334af16f8d9 100644
--- a/require.Makefile
+++ b/require.Makefile
@@ -41,6 +41,7 @@ APPSRC := $(APP)/src
 APPDB := $(APP)/Db
 
 SOURCES += $(APPSRC)/require.c
+SOURCES += $(APPSRC)/version.c
 DBDS    += $(APPSRC)/require.dbd
 SOURCES += $(APPSRC)/runScript.c
 DBDS    += $(APPSRC)/runScript.dbd