Thu Mar 12 19:45:06 2020 UTC ()
pkgtools/check-portability: explain how to fix the errors


(rillig)
diff -r1.6 -r1.7 pkgsrc/pkgtools/check-portability/files/check-portability.c

cvs diff -r1.6 -r1.7 pkgsrc/pkgtools/check-portability/files/check-portability.c (expand / switch to unified diff)

--- pkgsrc/pkgtools/check-portability/files/check-portability.c 2020/03/12 19:26:17 1.6
+++ pkgsrc/pkgtools/check-portability/files/check-portability.c 2020/03/12 19:45:06 1.7
@@ -1,14 +1,14 @@ @@ -1,14 +1,14 @@
1/* $NetBSD: check-portability.c,v 1.6 2020/03/12 19:26:17 rillig Exp $ */ 1/* $NetBSD: check-portability.c,v 1.7 2020/03/12 19:45:06 rillig Exp $ */
2 2
3/* 3/*
4 Copyright (c) 2020 Roland Illig 4 Copyright (c) 2020 Roland Illig
5 All rights reserved. 5 All rights reserved.
6 6
7 Redistribution and use in source and binary forms, with or without 7 Redistribution and use in source and binary forms, with or without
8 modification, are permitted provided that the following conditions 8 modification, are permitted provided that the following conditions
9 are met: 9 are met:
10 10
11 1. Redistributions of source code must retain the above copyright 11 1. Redistributions of source code must retain the above copyright
12 notice, this list of conditions and the following disclaimer. 12 notice, this list of conditions and the following disclaimer.
13 2. Redistributions in binary form must reproduce the above copyright 13 2. Redistributions in binary form must reproduce the above copyright
14 notice, this list of conditions and the following disclaimer in the 14 notice, this list of conditions and the following disclaimer in the
@@ -228,51 +228,69 @@ static bool @@ -228,51 +228,69 @@ static bool
228str_read_text_line(str *s, FILE *f) 228str_read_text_line(str *s, FILE *f)
229{ 229{
230 int c; 230 int c;
231 231
232 s->len = 0; 232 s->len = 0;
233 while ((c = fgetc(f)) > '\0' && c != '\n' && c != '\r') { 233 while ((c = fgetc(f)) > '\0' && c != '\n' && c != '\r') {
234 str_append_char(s, (char) c); 234 str_append_char(s, (char) c);
235 } 235 }
236 assert(c != '\0'); 236 assert(c != '\0');
237 return c != EOF; 237 return c != EOF;
238} 238}
239 239
240typedef enum { 240typedef enum {
 241 W_how_to_fix,
241 W_dollar_random, 242 W_dollar_random,
242 W_test_eqeq, 243 W_test_eqeq,
243 W_double_bracket 244 W_double_bracket
244} warning_kind; 245} warning_kind;
245 246
246static unsigned long explained = 0; 247static unsigned long explained = 0;
247 248
248static void 249static void
249explain(warning_kind warning, ...) 250explain(warning_kind warning, ...)
250{ 251{
251 unsigned long mask = 1UL << warning; 252 unsigned long mask = 1UL << warning;
252 if ((explained & mask) != 0) 253 if ((explained & mask) != 0)
253 return; 254 return;
254 explained |= mask; 255 explained |= mask;
255 256
256 va_list args; 257 va_list args;
257 va_start(args, warning); 258 va_start(args, warning);
258 printf("\n"); 259 printf("\n");
259 const char *line; 260 const char *line;
260 while ((line = va_arg(args, const char *)) != nullptr) 261 while ((line = va_arg(args, const char *)) != nullptr)
261 printf("%s%s\n", line[0] == '\0' ? "" : "\t", line); 262 printf("%s%s\n", line[0] == '\0' ? "" : "\t", line);
262 printf("\n"); 263 printf("\n");
263 va_end(args); 264 va_end(args);
264} 265}
265 266
 267static void
 268explain_how_to_fix(void)
 269{
 270 explain(
 271 W_how_to_fix,
 272 "To fix this message, decide whether this file is necessary",
 273 "for the package to build. Then choose any of these variants:",
 274 "",
 275 "1. Add a patch for the file",
 276 " (see https://www.netbsd.org/docs/pkgsrc/pkgsrc.html#components.patches)",
 277 "2. Add a SUBST block for the file to the package Makefile",
 278 " (see mk/subst.mk)",
 279 "3. Add CHECK_PORTABILITY_SKIP+= shell/glob to the package Makefile",
 280 " (see mk/check/check-portability.mk)",
 281 nullptr);
 282}
 283
266static size_t 284static size_t
267index_opening_bracket(cstr s) 285index_opening_bracket(cstr s)
268{ 286{
269 size_t index = cstr_index(s, CSTR("[[")); 287 size_t index = cstr_index(s, CSTR("[["));
270 if (index == npos) 288 if (index == npos)
271 return npos; 289 return npos;
272 if (index > 0 && !is_hspace(s.data[index - 1])) 290 if (index > 0 && !is_hspace(s.data[index - 1]))
273 return npos; 291 return npos;
274 if (index + 2 < s.len && !is_hspace(s.data[index + 2])) 292 if (index + 2 < s.len && !is_hspace(s.data[index + 2]))
275 return npos; 293 return npos;
276 return index; 294 return index;
277} 295}
278 296
@@ -318,26 +336,27 @@ checkline_sh_double_brackets(cstr filena @@ -318,26 +336,27 @@ checkline_sh_double_brackets(cstr filena
318 cstr_charptr(filename), lineno, opening_index + 1, 336 cstr_charptr(filename), lineno, opening_index + 1,
319 cstr_charptr(line)); 337 cstr_charptr(line));
320 nerrors++; 338 nerrors++;
321 explain( 339 explain(
322 W_double_bracket, 340 W_double_bracket,
323 "The keyword [[ is only available in bash, not in other shells.", 341 "The keyword [[ is only available in bash, not in other shells.",
324 "Since it is typically used inside an if statement, if that", 342 "Since it is typically used inside an if statement, if that",
325 "command is missing, it is interpreted as a \"no\".", 343 "command is missing, it is interpreted as a \"no\".",
326 "", 344 "",
327 "An error message of the form \"[[: command not found\"", 345 "An error message of the form \"[[: command not found\"",
328 "is logged, but that is easy to overlook in the large", 346 "is logged, but that is easy to overlook in the large",
329 "output of the build process.", 347 "output of the build process.",
330 nullptr); 348 nullptr);
 349 explain_how_to_fix();
331} 350}
332 351
333// Check for $RANDOM, which is specific to ksh and bash. 352// Check for $RANDOM, which is specific to ksh and bash.
334static void 353static void
335checkline_sh_dollar_random(cstr filename, size_t lineno, cstr line) 354checkline_sh_dollar_random(cstr filename, size_t lineno, cstr line)
336{ 355{
337 // Note: This code does not find _all_ instances of 356 // Note: This code does not find _all_ instances of
338 // unportable code. If a single line contains an unsafe and 357 // unportable code. If a single line contains an unsafe and
339 // a safe usage of $RANDOM, it will pass the test. 358 // a safe usage of $RANDOM, it will pass the test.
340 if (is_shell_comment(line)) 359 if (is_shell_comment(line))
341 return; 360 return;
342 if (!cstr_contains(line, CSTR("$RANDOM"))) 361 if (!cstr_contains(line, CSTR("$RANDOM")))
343 return; 362 return;
@@ -354,26 +373,27 @@ checkline_sh_dollar_random(cstr filename @@ -354,26 +373,27 @@ checkline_sh_dollar_random(cstr filename
354 if (idx != npos && idx + 7 < line.len && is_alnum(line.data[idx + 7])) 373 if (idx != npos && idx + 7 < line.len && is_alnum(line.data[idx + 7]))
355 return; 374 return;
356 375
357 printf("%s:%zu:%zu: $RANDOM: %s\n", 376 printf("%s:%zu:%zu: $RANDOM: %s\n",
358 cstr_charptr(filename), lineno, idx + 1, 377 cstr_charptr(filename), lineno, idx + 1,
359 cstr_charptr(line)); 378 cstr_charptr(line));
360 explain( 379 explain(
361 W_dollar_random, 380 W_dollar_random,
362 "The variable $RANDOM is not required for a POSIX-conforming shell, and", 381 "The variable $RANDOM is not required for a POSIX-conforming shell, and",
363 "many implementations of /bin/sh do not support it. It should therefore", 382 "many implementations of /bin/sh do not support it. It should therefore",
364 "not be used in shell programs that are meant to be portable across a", 383 "not be used in shell programs that are meant to be portable across a",
365 "large number of POSIX-like systems.", 384 "large number of POSIX-like systems.",
366 nullptr); 385 nullptr);
 386 explain_how_to_fix();
367} 387}
368 388
369typedef void (*foreach_3_fields_cb)(cstr f1, cstr f2, cstr f3, void *actiondata); 389typedef void (*foreach_3_fields_cb)(cstr f1, cstr f2, cstr f3, void *actiondata);
370 390
371static void 391static void
372foreach_3_fields_in_line(cstr line, foreach_3_fields_cb action, void *actiondata) 392foreach_3_fields_in_line(cstr line, foreach_3_fields_cb action, void *actiondata)
373{ 393{
374 size_t idx = 0; 394 size_t idx = 0;
375 cstr f1 = cstr_next_field(line, &idx); 395 cstr f1 = cstr_next_field(line, &idx);
376 cstr f2 = cstr_next_field(line, &idx); 396 cstr f2 = cstr_next_field(line, &idx);
377 cstr f3 = cstr_next_field(line, &idx); 397 cstr f3 = cstr_next_field(line, &idx);
378 398
379 while (f3.len > 0) { 399 while (f3.len > 0) {
@@ -403,33 +423,28 @@ checkline_sh_test_eqeq_action(cstr f1, c @@ -403,33 +423,28 @@ checkline_sh_test_eqeq_action(cstr f1, c
403 "%s:%zu:%zu: found test ... == ...: %s\n", 423 "%s:%zu:%zu: found test ... == ...: %s\n",
404 cstr_charptr(ad->filename), ad->lineno, 424 cstr_charptr(ad->filename), ad->lineno,
405 (size_t) (f3.data - ad->line.data), 425 (size_t) (f3.data - ad->line.data),
406 cstr_charptr(ad->line)); 426 cstr_charptr(ad->line));
407 explain( 427 explain(
408 W_test_eqeq, 428 W_test_eqeq,
409 "The \"test\" command, as well as the \"[\" command, are not required to know", 429 "The \"test\" command, as well as the \"[\" command, are not required to know",
410 "the \"==\" operator. Only a few implementations like bash and some", 430 "the \"==\" operator. Only a few implementations like bash and some",
411 "versions of ksh support it.", 431 "versions of ksh support it.",
412 "", 432 "",
413 "When you run \"test foo == foo\" on a platform that does not support the", 433 "When you run \"test foo == foo\" on a platform that does not support the",
414 "\"==\" operator, the result will be \"false\" instead of \"true\". This can", 434 "\"==\" operator, the result will be \"false\" instead of \"true\". This can",
415 "lead to unexpected behavior.", 435 "lead to unexpected behavior.",
416 "", 
417 "There are two ways to fix this error message. If the file that contains", 
418 "the \"test ==\" is needed for building the package, you should create a", 
419 "patch for it, replacing the \"==\" operator with \"=\". If the file is not", 
420 "needed, add its name to the CHECK_PORTABILITY_SKIP variable in the", 
421 "package Makefile.", 
422 nullptr); 436 nullptr);
 437 explain_how_to_fix();
423} 438}
424 439
425static void 440static void
426checkline_sh_test_eqeq(cstr filename, size_t lineno, cstr line) 441checkline_sh_test_eqeq(cstr filename, size_t lineno, cstr line)
427{ 442{
428 if (is_shell_comment(line)) 443 if (is_shell_comment(line))
429 return; 444 return;
430 445
431 struct checkline_sh_test_eqeq_actiondata ad = { filename, lineno, line }; 446 struct checkline_sh_test_eqeq_actiondata ad = { filename, lineno, line };
432 foreach_3_fields_in_line(line, checkline_sh_test_eqeq_action, &ad); 447 foreach_3_fields_in_line(line, checkline_sh_test_eqeq_action, &ad);
433} 448}
434 449
435static bool 450static bool