/*!A cross-platform build utility based on Lua * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * Copyright (C) 2015-present, Xmake Open Source Community. * * @author uael * @file select.c * */ /* ////////////////////////////////////////////////////////////////////////////////////// * trace */ #define TB_TRACE_MODULE_NAME "select" #define TB_TRACE_MODULE_DEBUG (0) /* ////////////////////////////////////////////////////////////////////////////////////// * includes */ #include "prefix.h" /* ////////////////////////////////////////////////////////////////////////////////////// * private implementation */ static tb_bool_t xm_semver_select_from_versions_tags1( lua_State *lua, tb_int_t fromidx, semver_t *semver, semver_range_t const *range, semvers_t *matches) { // clear matches semvers_pclear(matches); // select all matches lua_Integer i = 0; luaL_checktype(lua, fromidx, LUA_TTABLE); for (i = lua_objlen(lua, fromidx); i > 0; --i) { lua_pushinteger(lua, i); lua_gettable(lua, fromidx); tb_char_t const *source_str = luaL_checkstring(lua, -1); if (source_str && semver_tryn(semver, source_str, tb_strlen(source_str)) == 0) { if (semver_range_pmatch(semver, range)) { semvers_ppush(matches, *semver); } else { semver_dtor(semver); } } lua_pop(lua, 1); } // no matches? tb_check_return_val(matches->length, tb_false); // sort matches semvers_psort(matches); // get the newest version semver_t top = semvers_ppop(matches); lua_createtable(lua, 0, 2); // return results lua_pushstring(lua, top.raw); lua_setfield(lua, -2, "version"); lua_pushstring(lua, fromidx == 2 ? "version" : "tag"); lua_setfield(lua, -2, "source"); // exit the popped semver semver_dtor(&top); return tb_true; } static tb_bool_t xm_semver_select_from_versions_tags2( lua_State *lua, tb_int_t fromidx, semver_t *semver, tb_char_t const *version_str, tb_size_t version_len) { lua_Integer i = 0; luaL_checktype(lua, fromidx, LUA_TTABLE); for (i = lua_objlen(lua, fromidx); i > 0; --i) { lua_pushinteger(lua, i); lua_gettable(lua, fromidx); tb_char_t const *source_str = luaL_checkstring(lua, -1); tb_size_t source_len = tb_strlen(source_str); lua_pop(lua, 1); if (source_len == version_len && tb_strncmp(source_str, version_str, version_len) == 0) { lua_createtable(lua, 0, 2); lua_pushlstring(lua, source_str, source_len); lua_setfield(lua, -2, "version"); lua_pushstring(lua, fromidx == 2 ? "version" : "tag"); lua_setfield(lua, -2, "source"); return tb_true; } } return tb_false; } static tb_bool_t xm_semver_select_from_branches(lua_State *lua, tb_int_t fromidx, tb_char_t const *range_str, tb_size_t range_len) { lua_Integer i = 0; luaL_checktype(lua, fromidx, LUA_TTABLE); for (i = lua_objlen(lua, fromidx); i > 0; --i) { lua_pushinteger(lua, i); lua_gettable(lua, fromidx); tb_char_t const *source_str = luaL_checkstring(lua, -1); tb_size_t source_len = tb_strlen(source_str); lua_pop(lua, 1); if (source_len == range_len && tb_memcmp(source_str, range_str, source_len) == 0) { lua_createtable(lua, 0, 2); lua_pushlstring(lua, source_str, source_len); lua_setfield(lua, -2, "version"); lua_pushstring(lua, "branch"); lua_setfield(lua, -2, "source"); return tb_true; } } return tb_false; } static tb_bool_t xm_semver_select_latest_from_versions_tags(lua_State *lua, tb_int_t fromidx, semver_t *semver, semvers_t *matches) { // clear matches semvers_pclear(matches); // push all versions to matches lua_Integer i = 0; luaL_checktype(lua, fromidx, LUA_TTABLE); for (i = lua_objlen(lua, fromidx); i > 0; --i) { lua_pushinteger(lua, i); lua_gettable(lua, fromidx); tb_char_t const *source_str = luaL_checkstring(lua, -1); if (source_str && semver_tryn(semver, source_str, tb_strlen(source_str)) == 0) { semvers_ppush(matches, *semver); } lua_pop(lua, 1); } tb_check_return_val(matches->length, tb_false); // sort matches semvers_psort(matches); // get the newest match semver_t top = semvers_ppop(matches); lua_createtable(lua, 0, 2); // return results lua_pushstring(lua, top.raw); lua_setfield(lua, -2, "version"); lua_pushstring(lua, fromidx == 2 ? "version" : "tag"); lua_setfield(lua, -2, "source"); semver_dtor(&top); return tb_true; } /* ////////////////////////////////////////////////////////////////////////////////////// * implementation */ /* select version * * local versioninfo, errors = semver.select(">=1.5.0 <1.6", {"1.5.0", "1.5.1"}, {"v1.5.0", ..}, {"latest", "dev"}) */ tb_int_t xm_semver_select(lua_State *lua) { tb_assert_and_check_return_val(lua, 0); // select version tb_bool_t ok = tb_false; tb_bool_t is_range = tb_false; tb_char_t const *range_str = tb_null; semver_t semver = { 0 }; semvers_t matches = { 0 }; semver_range_t range = { 0 }; do { // get the version range string range_str = luaL_checkstring(lua, 1); tb_check_break(range_str); // get the range string length tb_size_t range_len = tb_strlen(range_str); // parse the version range string is_range = semver_rangen(&range, range_str, range_len) == 0; if (is_range) { // attempt to select version from the versions list first if (xm_semver_select_from_versions_tags1(lua, 2, &semver, &range, &matches)) { ok = tb_true; break; } // attempt to select version from the tags list if (xm_semver_select_from_versions_tags1(lua, 3, &semver, &range, &matches)) { ok = tb_true; break; } } else { // attempt to select version from the versions list first if (xm_semver_select_from_versions_tags2(lua, 2, &semver, range_str, range_len)) { ok = tb_true; break; } // attempt to select version from the tags list if (xm_semver_select_from_versions_tags2(lua, 3, &semver, range_str, range_len)) { ok = tb_true; break; } } // attempt to select version from the branches if (xm_semver_select_from_branches(lua, 4, range_str, range_len)) { ok = tb_true; break; } // select the latest version from the tags and versions if be latest if (!tb_strcmp(range_str, "latest")) { // attempt to select latest version from the versions list if (xm_semver_select_latest_from_versions_tags(lua, 2, &semver, &matches)) { ok = tb_true; break; } // attempt to select latest version from the tags list if (xm_semver_select_latest_from_versions_tags(lua, 3, &semver, &matches)) { ok = tb_true; break; } } } while (0); // exit matches semvers_dtor(matches); // exit range semver_range_dtor(&range); // failed? if (!ok) { if (!is_range) { lua_pushnil(lua); lua_pushfstring(lua, "unable to parse semver range '%s'", range_str); } lua_pushnil(lua); lua_pushfstring(lua, "unable to select version for range '%s'", range_str); return 2; } return 1; }