Commit 0c56aae2 authored by Petr Špaček's avatar Petr Špaček

Merge branch 'stricter-ci-build' into 'master'

ci: add -Werror to CFLAGS, added clang build target

See merge request knot/knot-resolver!432
parents a60d0652 8b5b7109
---
Checks: 'bugprone-*,cert-*,-clang-analyzer-unix.Malloc,-clang-analyzer-deadcode.DeadStores,-clang-analyzer-valist.Uninitialized,readability-*,-readability-braces-*,-readability-else-after-return,-readability-redundant-declaration,-readability-non-const-parameter,google-readability-casting,misc-*,-misc-macro-parentheses,-misc-unused-parameters'
WarningsAsErrors: 'cert-*,misc-*,readability-*,clang-analyzer-*,-readability-non-const-parameter'
HeaderFilterRegex: 'contrib/ucw/*.h'
CheckOptions:
- key: readability-identifier-naming
value: 'lower_case'
- key: readability-function-size.StatementThreshold
value: '400'
- key: readability-function-size.LineThreshold
value: '500'
\ No newline at end of file
......@@ -15,8 +15,8 @@ stages:
build:linux:amd64:
stage: build
script:
- PREFIX=$(pwd)/.local make -k all
- PREFIX=$(pwd)/.local make install
- PREFIX=$(pwd)/.local make -k all CFLAGS=-Werror
- PREFIX=$(pwd)/.local make install CFLAGS=-Werror
artifacts:
untracked: true
tags:
......@@ -24,11 +24,31 @@ build:linux:amd64:
- linux
- amd64
build:clang:linux:amd64:
stage: build
image: $CI_REGISTRY/knot/knot-resolver/ci:debian-unstable # newer Debian for newer Clang
script:
- CXX=clang++-5.0 CC=clang-5.0 PREFIX=$(pwd)/.local make -k all CFLAGS=-Werror
- CXX=clang++-5.0 CC=clang-5.0 PREFIX=$(pwd)/.local make install CFLAGS=-Werror
tags:
- docker
- linux
- amd64
lint:lua:
stage: test
dependencies: [] # do not download build artifacts
script:
- make lint
- make lint-lua
tags:
- docker
lint:c:
stage: test
image: $CI_REGISTRY/knot/knot-resolver/ci:debian-unstable # newer Debian for newer Clang
dependencies: [] # do not download build artifacts
script:
- make lint-c CLANG_TIDY="clang-tidy-5.0 -quiet"
tags:
- docker
......
......@@ -8,7 +8,9 @@ check: all tests
clean: contrib-clean lib-clean daemon-clean client-clean modules-clean \
tests-clean doc-clean bench-clean coverage-clean
doc: doc-html
lint: $(patsubst %.lua.in,%.lua,$(wildcard */*/*.lua.in))
lint: lint-lua lint-c
lint-c: libkres-lint kresd-lint kresc-lint
lint-lua: $(patsubst %.lua.in,%.lua,$(wildcard */*/*.lua.in))
luacheck --codes --formatter TAP .
.PHONY: all install check clean doc info lint
......
......@@ -68,4 +68,4 @@ RUN apt-get install -y -qqq lcov
RUN luarocks install luacov
# LuaJIT binary for stand-alone scripting
RUN apt-get install -y -qqq luajit
RUN apt-get install -y -qqq luajit
\ No newline at end of file
......@@ -35,7 +35,7 @@
FILE *g_tty = NULL; //!< connection to the daemon
static char *run_cmd(const char *cmd, size_t * msg_len);
static char *run_cmd(const char *cmd, size_t * out_len);
const char *prompt(EditLine * e)
{
......@@ -273,14 +273,14 @@ static unsigned char complete(EditLine * el, int ch)
//Get position of last dot in current line (useful for parsing table).
char *dot = strrchr(argv[0], '.');
if (strncmp(type, "table", 5) && !dot) {
if (strncmp(type, "table", 5) != 0 && !dot) {
//Line is not a name of some table and there is no dot in it.
complete_globals(el, argv[0], pos);
} else if ((dot && !strncmp(type, "nil", 3))
|| !strncmp(type, "table", 5)) {
} else if ((dot && strncmp(type, "nil", 3) == 0)
|| strncmp(type, "table", 5) == 0) {
//Current line (or part of it) is a name of some table.
complete_members(el, argv[0], type, pos, dot);
} else if (!strncmp(type, "function", 8)) {
} else if (strncmp(type, "function", 8) == 0) {
//Current line is a function.
complete_function(el);
}
......
......@@ -913,7 +913,7 @@ static int cache_prefixed(struct kr_cache *cache, const char *args, knot_db_val_
{
/* Decode parameters */
uint8_t namespace = 'R';
char *extra = (char *)strchr(args, ' ');
char *extra = strchr(args, ' ');
if (extra != NULL) {
extra[0] = '\0';
namespace = extra[1];
......
......@@ -19,6 +19,7 @@ ifeq ($(AMALG), yes)
kresd.amalg.c: daemon/lua/sandbox.inc daemon/lua/config.inc
else
daemon/engine.o: daemon/lua/sandbox.inc daemon/lua/config.inc
kresd-lint: daemon/lua/sandbox.inc daemon/lua/config.inc
endif
# Installed FFI bindings
......
......@@ -86,8 +86,8 @@ int engine_ipc(struct engine *engine, const char *expr);
*/
int engine_start(struct engine *engine, const char *config_path);
void engine_stop(struct engine *engine);
int engine_register(struct engine *engine, const char *module, const char *precedence, const char* ref);
int engine_unregister(struct engine *engine, const char *module);
int engine_register(struct engine *engine, const char *name, const char *precedence, const char* ref);
int engine_unregister(struct engine *engine, const char *name);
void engine_lualib(struct engine *engine, const char *name, int (*lib_cb) (struct lua_State *));
......
......@@ -170,14 +170,14 @@ void udp_recv(uv_udp_t *handle, ssize_t nread, const uv_buf_t *buf,
static int udp_bind_finalize(uv_handle_t *handle)
{
check_bufsize((uv_handle_t *)handle);
check_bufsize(handle);
/* Handle is already created, just create context. */
struct session *session = session_new();
assert(session);
session->outgoing = false;
session->handle = handle;
handle->data = session;
return io_start_read((uv_handle_t *)handle);
return io_start_read(handle);
}
int udp_bind(uv_udp_t *handle, struct sockaddr *addr)
......
......@@ -295,7 +295,7 @@ static const char *set_addr(char *addr, int *port)
p = strchr(addr, '#');
}
if (p) {
*port = atoi(p + 1);
*port = strtol(p + 1, NULL, 10);
*p = '\0';
}
......@@ -465,17 +465,17 @@ int main(int argc, char **argv)
array_push(tls_set, optarg);
break;
case 'S':
array_push(fd_set, atoi(optarg));
array_push(fd_set, strtol(optarg, NULL, 10));
break;
case 'T':
array_push(tls_fd_set, atoi(optarg));
array_push(tls_fd_set, strtol(optarg, NULL, 10));
break;
case 'c':
config = optarg;
break;
case 'f':
g_interactive = false;
forks = atoi(optarg);
forks = strtol(optarg, NULL, 10);
if (forks <= 0) {
kr_log_error("[system] error '-f' requires a positive"
" number, not '%s'\n", optarg);
......
......@@ -140,7 +140,7 @@ static int open_endpoint(struct network *net, struct endpoint *ep, struct sockad
{
int ret = 0;
if (flags & NET_UDP) {
ep->udp = malloc(sizeof(uv_handles_t));
ep->udp = malloc(sizeof(*ep->udp));
if (!ep->udp) {
return kr_error(ENOMEM);
}
......@@ -153,7 +153,7 @@ static int open_endpoint(struct network *net, struct endpoint *ep, struct sockad
ep->flags |= NET_UDP;
}
if (flags & NET_TCP) {
ep->tcp = malloc(sizeof(uv_handles_t));
ep->tcp = malloc(sizeof(*ep->tcp));
if (!ep->tcp) {
return kr_error(ENOMEM);
}
......@@ -185,7 +185,7 @@ static int open_endpoint_fd(struct network *net, struct endpoint *ep, int fd, in
if (ep->udp) {
return kr_error(EEXIST);
}
ep->udp = malloc(sizeof(uv_handles_t));// malloc(sizeof(*ep->udp));
ep->udp = malloc(sizeof(*ep->udp));
if (!ep->udp) {
return kr_error(ENOMEM);
}
......@@ -201,7 +201,7 @@ static int open_endpoint_fd(struct network *net, struct endpoint *ep, int fd, in
if (ep->tcp) {
return kr_error(EEXIST);
}
ep->tcp = malloc(sizeof(uv_handles_t));
ep->tcp = malloc(sizeof(*ep->tcp));
if (!ep->tcp) {
return kr_error(ENOMEM);
}
......@@ -248,7 +248,7 @@ int network_listen_fd(struct network *net, int fd, bool use_tls)
return kr_error(EBADF);
}
/* Extract local address for this socket. */
struct sockaddr_storage ss;
struct sockaddr_storage ss = { .ss_family = AF_UNSPEC };
socklen_t addr_len = sizeof(ss);
ret = getsockname(fd, (struct sockaddr *)&ss, &addr_len);
if (ret != 0) {
......
......@@ -71,7 +71,7 @@ int tls_process(struct worker_ctx *worker, uv_stream_t *handle, const uint8_t *b
int tls_certificate_set(struct network *net, const char *tls_cert, const char *tls_key);
/*! Borrow TLS credentials for context. */
struct tls_credentials *tls_credentials_reserve(struct tls_credentials *worker);
struct tls_credentials *tls_credentials_reserve(struct tls_credentials *tls_credentials);
/*! Release TLS credentials for context (decrements refcount or frees). */
int tls_credentials_release(struct tls_credentials *tls_credentials);
......
......@@ -104,14 +104,14 @@ static int worker_add_tcp_connected(struct worker_ctx *worker,
static int worker_del_tcp_connected(struct worker_ctx *worker,
const struct sockaddr *addr);
static struct session* worker_find_tcp_connected(struct worker_ctx *worker,
const struct sockaddr *srv);
const struct sockaddr *addr);
static int worker_add_tcp_waiting(struct worker_ctx *worker,
const struct sockaddr *addr,
struct session *session);
static int worker_del_tcp_waiting(struct worker_ctx *worker,
const struct sockaddr *addr);
static struct session* worker_find_tcp_waiting(struct worker_ctx *worker,
const struct sockaddr *srv);
const struct sockaddr *addr);
static int session_add_waiting(struct session *session, struct qr_task *task);
static int session_del_waiting(struct session *session, struct qr_task *task);
static int session_add_tasks(struct session *session, struct qr_task *task);
......@@ -820,8 +820,7 @@ static int qr_task_on_send(struct qr_task *task, uv_handle_t *handle, int status
}
if (session->waiting.len > 0) {
struct qr_task *t = session->waiting.at[0];
int ret = qr_task_send(t, (uv_handle_t *)handle,
&session->peer.ip, t->pktbuf);
int ret = qr_task_send(t, handle, &session->peer.ip, t->pktbuf);
if (ret == kr_ok()) {
uv_timer_t *timer = &session->timeout;
uv_timer_stop(timer);
......@@ -1055,7 +1054,7 @@ static void on_connect(uv_connect_t *req, int status)
struct session *session = handle->data;
union inaddr *peer = &session->peer;
uv_timer_stop((uv_timer_t *)&session->timeout);
uv_timer_stop(&session->timeout);
if (status == UV_ECANCELED) {
worker_del_tcp_waiting(worker, &peer->ip);
......@@ -1308,7 +1307,7 @@ static void on_retransmit(uv_timer_t *req)
static int timer_start(struct session *session, uv_timer_cb cb,
uint64_t timeout, uint64_t repeat)
{
uv_timer_t *timer = (uv_timer_t *)&session->timeout;
uv_timer_t *timer = &session->timeout;
assert(timer->data == session);
int ret = uv_timer_start(timer, cb, timeout, repeat);
if (ret != 0) {
......@@ -1701,7 +1700,7 @@ static struct qr_task* find_task(const struct session *session, uint16_t msg_id)
int worker_submit(struct worker_ctx *worker, uv_handle_t *handle,
knot_pkt_t *msg, const struct sockaddr* addr)
knot_pkt_t *query, const struct sockaddr* addr)
{
bool OK = worker && handle && handle->data;
if (!OK) {
......@@ -1712,15 +1711,15 @@ int worker_submit(struct worker_ctx *worker, uv_handle_t *handle,
struct session *session = handle->data;
/* Parse packet */
int ret = parse_packet(msg);
int ret = parse_packet(query);
/* Start new task on listening sockets,
* or resume if this is subrequest */
struct qr_task *task = NULL;
if (!session->outgoing) { /* request from a client */
/* Ignore badly formed queries or responses. */
if (!msg || ret != 0 || knot_wire_get_qr(msg->wire)) {
if (msg) worker->stats.dropped += 1;
if (!query || ret != 0 || knot_wire_get_qr(query->wire)) {
if (query) worker->stats.dropped += 1;
return kr_error(EILSEQ);
}
struct request_ctx *ctx = request_create(worker, handle, addr);
......@@ -1728,7 +1727,7 @@ int worker_submit(struct worker_ctx *worker, uv_handle_t *handle,
return kr_error(ENOMEM);
}
ret = request_start(ctx, msg);
ret = request_start(ctx, query);
if (ret != 0) {
request_free(ctx);
return kr_error(ENOMEM);
......@@ -1739,8 +1738,8 @@ int worker_submit(struct worker_ctx *worker, uv_handle_t *handle,
request_free(ctx);
return kr_error(ENOMEM);
}
} else if (msg) { /* response from upstream */
task = find_task(session, knot_wire_get_id(msg->wire));
} else if (query) { /* response from upstream */
task = find_task(session, knot_wire_get_id(query->wire));
if (task == NULL) {
return kr_error(ENOENT);
}
......@@ -1749,7 +1748,7 @@ int worker_submit(struct worker_ctx *worker, uv_handle_t *handle,
assert(uv_is_closing(session->handle) == false);
/* Consume input and produce next message */
return qr_task_step(task, NULL, msg);
return qr_task_step(task, NULL, query);
}
static int map_add_tcp_session(map_t *map, const struct sockaddr* addr,
......@@ -1863,7 +1862,7 @@ int worker_end_tcp(struct worker_ctx *worker, uv_handle_t *handle)
* borrowed the task from parent session. */
struct session *session = handle->data;
if (session->outgoing) {
worker_submit(worker, (uv_handle_t *)handle, NULL, NULL);
worker_submit(worker, handle, NULL, NULL);
} else {
discard_buffered(session);
}
......
......@@ -62,7 +62,8 @@ There are also *optional* packages that enable specific functionality in Knot DN
"libprotobuf_ 3.0+", "``modules/dnstap``", "Protocol Buffers support for dnstap_."
"`libprotobuf-c`_ 1.0+", "``modules/dnstap``", "C bindings for Protobuf."
"libfstrm_ 0.2+", "``modules/dnstap``", "Frame Streams data transport protocol."
"luacheck_", "``lint``", "Syntax and static analysis checker for Lua."
"luacheck_", "``lint-lua``", "Syntax and static analysis checker for Lua."
"`clang-tidy`_", "``lint-c``", "Syntax and static analysis checker for C."
"luacov_", "``check-config``", "Code coverage analysis for Lua modules."
.. [#] Requires C99, ``__attribute__((cleanup))`` and ``-MMD -MP`` for dependency file generation. GCC, Clang and ICC are supported.
......@@ -260,7 +261,7 @@ The `make coverage` target gathers both gcov code coverage for C files, and luac
Running unit and integration tests
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The linter requires luacheck_ and is executed by ``make lint``.
The linter requires luacheck_ and `clang-tidy`_ and is executed by ``make lint``.
The unit tests require cmocka_ and are executed by ``make check``.
Tests for the dnstap module need go and are executed by ``make ckeck-dnstap``.
......@@ -329,6 +330,7 @@ You can hack on the container by changing the container entrypoint to shell like
.. _libprotobuf-c: https://github.com/protobuf-c/protobuf-c/wiki
.. _libfstrm: https://github.com/farsightsec/fstrm
.. _luacheck: http://luacheck.readthedocs.io
.. _clang-tidy: http://clang.llvm.org/extra/clang-tidy/index.html
.. _luacov: https://keplerproject.github.io/luacov/
.. _lcov: http://ltp.sourceforge.net/coverage/lcov.php
......
......@@ -290,7 +290,7 @@ int kr_cache_clear(struct kr_cache *cache)
return ret;
}
int kr_cache_match(struct kr_cache *cache, uint8_t tag, const knot_dname_t *name, knot_db_val_t *val, int maxcount)
int kr_cache_match(struct kr_cache *cache, uint8_t tag, const knot_dname_t *name, knot_db_val_t *vals, int valcnt)
{
if (!cache_isvalid(cache) || !name ) {
return kr_error(EINVAL);
......@@ -307,7 +307,7 @@ int kr_cache_match(struct kr_cache *cache, uint8_t tag, const knot_dname_t *name
/* Trim type from the search key */
knot_db_val_t key = { keybuf, key_len - 2 };
return cache_op(cache, match, &key, val, maxcount);
return cache_op(cache, match, &key, vals, valcnt);
}
int kr_cache_peek_rr(struct kr_cache *cache, knot_rrset_t *rr, uint8_t *rank, uint8_t *flags, uint32_t *timestamp)
......
......@@ -47,7 +47,7 @@ typedef unsigned int uint;
#define kr_ok() 0
/* Mark as cold to mark all branches as unlikely. */
static inline int KR_COLD kr_error(int x) {
return -abs(x);
return x <= 0 ? x : -x;
}
#define kr_strerror(x) strerror(abs(x))
......
......@@ -333,7 +333,7 @@ int kr_dnskeys_trusted(kr_rrset_validation_ctx_t *vctx, const knot_rrset_t *ta)
continue;
}
struct dseckey *key;
struct dseckey *key = NULL;
if (kr_dnssec_key_from_rdata(&key, keys->owner, key_data, knot_rdata_rdlen(krr)) != 0) {
continue;
}
......
......@@ -141,6 +141,9 @@ static int name_error_response_check_rr(int *flags, const knot_rrset_t *nsec,
while (ptr[0]) {
/* Remove leftmost label and replace it with '\1*'. */
ptr = (uint8_t *) knot_wire_next_label(ptr, NULL);
if (!ptr) {
return kr_error(EINVAL);
}
*(--ptr) = '*';
*(--ptr) = 1;
/* True if this wildcard provably doesn't exist. */
......
......@@ -222,7 +222,7 @@ static int covers_name(int *flags, const knot_rrset_t *nsec3, const knot_dname_t
uint8_t *next_hash = NULL;
knot_nsec3_next_hashed(&nsec3->rrs, 0, &next_hash, &next_size);
if ((owner_hash.size == next_size) && (name_hash.size == next_size)) {
if ((next_size > 0) && (owner_hash.size == next_size) && (name_hash.size == next_size)) {
/* All hash lengths must be same. */
const uint8_t *ownrd = owner_hash.data;
const uint8_t *nextd = next_hash;
......
......@@ -210,6 +210,7 @@ static int sign_ctx_add_records(dnssec_sign_ctx_t *ctx, const knot_rrset_t *cove
for (int j = 0; j < trim_labels; ++j) {
assert(beginp[0]);
beginp = (uint8_t *) knot_wire_next_label(beginp, NULL);
assert(beginp != NULL);
}
*(--beginp) = '*';
*(--beginp) = 1;
......
......@@ -152,7 +152,7 @@ KR_EXPORT void * lru_get_impl(struct lru *lru, const char *key, uint key_len,
if (g->hashes[i] == khash_top) {
it = g->items[i];
if (likely(it && it->key_len == key_len
&& memcmp(it->data, key, key_len) == 0))
&& (key_len == 0 || memcmp(it->data, key, key_len) == 0)))
goto found; // to reduce huge nesting depth
}
}
......@@ -200,7 +200,9 @@ insert: // insert into position i (incl. key)
}
it->key_len = key_len;
it->val_len = val_len;
memcpy(it->data, key, key_len);
if (key_len > 0) {
memcpy(it->data, key, key_len);
}
memset(item_val(it), 0, val_len); // clear the value
found: // key and hash OK on g->items[i]; now update stamps
assert(i < LRU_ASSOC);
......
......@@ -48,7 +48,7 @@ typedef struct {
} cb_node_t;
/* Return true if ptr is internal node. */
static inline int ref_is_internal(uint8_t *p)
static inline int ref_is_internal(const uint8_t *p)
{
return 1 & (intptr_t)p;
}
......@@ -168,7 +168,7 @@ EXPORT void *map_get(map_t *map, const char *str)
}
/*! Inserts str into map, returns 0 on success */
EXPORT int map_set(map_t *map, const char *str, void *value)
EXPORT int map_set(map_t *map, const char *str, void *val)
{
const uint8_t *const ubytes = (void *)str;
const size_t ulen = strlen(str);
......@@ -182,7 +182,7 @@ EXPORT int map_set(map_t *map, const char *str, void *value)
void **wherep = NULL;
if (p == NULL) {
map->root = cbt_make_data(map, (const uint8_t *)str, ulen + 1, value);
map->root = cbt_make_data(map, (const uint8_t *)str, ulen + 1, val);
if (map->root == NULL) {
return ENOMEM;
}
......@@ -212,7 +212,7 @@ EXPORT int map_set(map_t *map, const char *str, void *value)
newotherbits = data->key[newbyte];
goto different_byte_found;
}
data->value = value;
data->value = val;
return 1;
different_byte_found:
......@@ -228,7 +228,7 @@ different_byte_found:
return ENOMEM;
}
x = (uint8_t *)cbt_make_data(map, ubytes, ulen + 1, value);
x = (uint8_t *)cbt_make_data(map, ubytes, ulen + 1, val);
if (x == NULL) {
map->free(map->baton, newnode);
return ENOMEM;
......@@ -326,10 +326,14 @@ EXPORT void map_clear(map_t *map)
EXPORT int map_walk_prefixed(map_t *map, const char *prefix,
int (*callback)(const char *, void *, void *), void *baton)
{
if (!map) {
return 0;
}
const uint8_t *ubytes = (void *)prefix;
const size_t ulen = strlen(prefix);
uint8_t *p = map->root;
uint8_t *top = (uint8_t *)p;
uint8_t *top = p;
cb_data_t *data = NULL;
if (p == NULL) {
......
......@@ -400,6 +400,8 @@ int kr_nsrep_sort(struct kr_nsrep *ns, kr_nsrep_lru_t *cache)
}
}
/* At least two addresses must be in the address list */
assert(count > 0);
ns->score = scores[0];
ns->reputation = 0;
return kr_ok();
......
......@@ -1296,7 +1296,7 @@ static int zone_cut_check(struct kr_request *request, struct kr_query *qry, knot
} while (state == KR_STATE_CONSUME);
/* Update minimized QNAME if zone cut changed */
if (qry->zone_cut.name[0] != '\0' && !(qry->flags.NO_MINIMIZE)) {
if (qry->zone_cut.name && qry->zone_cut.name[0] != '\0' && !(qry->flags.NO_MINIMIZE)) {
if (kr_make_query(qry, packet) != 0) {
return KR_STATE_FAIL;
}
......
......@@ -108,6 +108,10 @@ void kr_log_verbose(const char *fmt, ...)
char* kr_strcatdup(unsigned n, ...)
{
if (n < 1) {
return NULL;
}
/* Calculate total length */
size_t total_len = 0;
va_list vl;
......@@ -402,7 +406,7 @@ int kr_straddr_subnet(void *dst, const char *addr)
if (subnet) {
*subnet = '\0';
subnet += 1;
bit_len = atoi(subnet);
bit_len = strtol(subnet, NULL, 10);
/* Check client subnet length */
const int max_len = (family == AF_INET6) ? 128 : 32;
if (bit_len < 0 || bit_len > max_len) {
......
......@@ -4,6 +4,7 @@
# Platform-dependent stuff checks
CCLD := $(CC)
CLANG_TIDY ?= clang-tidy -quiet
CGO := go tool cgo
GO := go
CAT := cat
......@@ -101,6 +102,9 @@ else
$(call quiet,CCLD,$$@) $$($(1)_CFLAGS) $(BUILD_CFLAGS) $$($(1)_OBJ) $(call SOVER,$(7),$(7),$(1)) -o $$@ $(4) $$($(1)_LIBS) $(BUILD_LDFLAGS) $$($(1)_LDFLAGS)
endif
endif
# Linter rules
$(1)-lint: $$($(1)_SOURCES)
$(call quiet,CLANG_TIDY,$(1)) $$($(1)_SOURCES) -- $(BUILD_CFLAGS) $$($(1)_CFLAGS) -DMP_FREELIST_SIZE=0 -D__clang_analyzer__
# Additional rules
$(1)-clean:
$(RM) $$($(1)_OBJ) $$($(1)_DEP) $(2)/$(1)$(3)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment