Forum Rule: Always post complete source code & details to reproduce any issue!
Page 1 of 2 1 2 LastLast
Results 1 to 25 of 28

Thread: Compile Error Trying to Use getopt()

  1. #1
    Senior Member
    Join Date
    Feb 2017
    Posts
    191

    Compile Error Trying to Use getopt()

    So, below is a stupid, do-nothing sketch, but it illustrates the problem. Throws the indicated error when compiled for T3.2 (Arduino 1.85, Teensyduino 1.44). The same sketch compiles error-free for a different ARM board like Adafruit Feather M0.

    Any advise appreciated.

    Thanks.

    The Code:
    Code:
    #include "Arduino.h"
    #include <unistd.h>
    
    void setup()
    {
      int16_t argc = 4;
      char **argv = nullptr, c;
      while ((c = getopt(argc, argv, "abc:")) != -1) {
      }
    }
    
    void loop()
    {
    }
    The Error:
    Code:
    Arduino: 1.8.5 (Windows 10), TD: 1.44, Board: "Teensy 3.2 / 3.1, Serial, 96 MHz (overclock), Faster, US English"
    
    Archiving built core (caching) in: C:\Users\tr001221\AppData\Local\Temp\arduino_cache_557088\core\core_teensy_avr_teensy31_usb_serial,speed_96,opt_o2std,keys_en-us_030978b2a1dfb34b2114ac9bd940b9a0.a
    c:/arduino-1.8.5/hardware/tools/arm/bin/../lib/gcc/arm-none-eabi/5.4.1/../../../../arm-none-eabi/lib/armv7e-m\libc.a(lib_a-writer.o): In function `_write_r':
    
    writer.c:(.text._write_r+0x12): undefined reference to `_write'
    
    collect2.exe: error: ld returned 1 exit status
    
    Error compiling for board Teensy 3.2 / 3.1.
    
    This report would have more information with
    "Show verbose output during compilation"
    option enabled in File -> Preferences.

  2. #2
    Senior Member PaulStoffregen's Avatar
    Join Date
    Nov 2012
    Posts
    18,683
    What are you expecting getopt() to actually do? Anything meaningful?!

  3. #3
    Senior Member
    Join Date
    Feb 2017
    Posts
    191
    Quote Originally Posted by PaulStoffregen View Post
    What are you expecting getopt() to actually do? Anything meaningful?!
    In this sketch, no (as I said "stupid, do-nothing"). It's simply syntactically-correct code that illustrates the compiler error. Once that is solved, I can go on with the real code.

  4. #4
    Senior Member+ Theremingenieur's Avatar
    Join Date
    Feb 2014
    Location
    Colmar, France
    Posts
    2,066
    If I remember correctly, getopt() is for parsing variable command line arguments for which I couldn't see any meaningful application in the embedded world. Thus, I wouldn't wonder if there was no support for this and similar functionalities in toolchains and libraries for MCUs.

  5. #5
    Senior Member
    Join Date
    Apr 2014
    Location
    Germany
    Posts
    307
    The libc which is linked in (newlib?) doesn't provide an implementation for _write(). This perfectly makes sense because the library can not know how to write anything anywhere on a given embedded system. So, you have to define that on your own. You could try something simple like

    Code:
    #include "Arduino.h"
    #include <unistd.h>
    
    extern "C" {
      int _write( int handle, char *buf, int count )
      {
        return Serial.write(buf, count);
      }
    }
    
    void setup()
    {
      int16_t argc = 4;
      char **argv = nullptr, c;
      while ((c = getopt(argc, argv, "abc:")) != -1) {
      }
    }
    
    void loop()
    {
    }
    Didn't test it so it might not work but at least it compiles :-)
    Last edited by luni; 11-29-2018 at 03:52 PM.

  6. #6
    Senior Member
    Join Date
    Feb 2017
    Posts
    191
    OK, thanks. When I get a chance I'll trace through the library code with an eye out for missing _write() implementation. That's a lot easier using Eclipse / Sleober than the dinky Arduino IDE. Like I said, my example compiles fine for Adafruit Feather M0, but not T3.2.

  7. #7
    Senior Member+ Frank B's Avatar
    Join Date
    Apr 2014
    Location
    Germany NRW
    Posts
    4,768
    And what should getopt return?
    Maybe while ( false ) does exactly the same...

  8. #8
    Senior Member
    Join Date
    Feb 2017
    Posts
    191
    Again, what I posted is not intended to be operational code. Simply a syntactically-correct example that produces the compiler error for T3.2 ---- but not when compiled for several M0 boards that I tried.

  9. #9
    Senior Member
    Join Date
    Jul 2014
    Posts
    1,939
    Paul, Thierry, Frank,
    he only did what the forum rules requires (code that reproduces the problem)

  10. #10
    Senior Member
    Join Date
    Jul 2014
    Location
    New York
    Posts
    1,941
    Just out of curiosity I tested the code posted by @luni. It compiles and runs but no output.

  11. #11
    Senior Member+ Frank B's Avatar
    Join Date
    Apr 2014
    Location
    Germany NRW
    Posts
    4,768
    I wonder what the mentioned several M0 boards do with getopt() - that's why asked. @Gfvalvo, what does getopt() do on these boards?If it's anything useful, I'd like to know!

  12. #12
    Senior Member
    Join Date
    Apr 2014
    Location
    Germany
    Posts
    307
    Just out of curiosity I tested the code posted by @luni. It compiles and runs but no output.
    Sure, the code obviously was not meant to be functional. Here a working example. OMG, writing this example felt like a flash back to the 80ies :-)) Never liked this getopt thing. And no, I also can not think of any useful application for embedded systems...

    Code:
    #include "Arduino.h"
    #include <unistd.h>
    
    extern "C"
    {
      // this is required for getopt to output error messages
      int _write(int handle, char *buf, int count)
      {   
        return Serial.write(buf, count);    
      }
    }
    
    void setup()
    {
      Serial.begin(0);
      while (!Serial);
    
      char *argv[] = {new char[10], new char[10], new char[10],new char[10],new char[10]};
      int argc = 5;
    
      strcpy(argv[0],"");
      strcpy(argv[1],"-b val");
      strcpy(argv[2],"-x");
      strcpy(argv[3],"-c");
      strcpy(argv[4],"-a");
    
      int option;
      while ((option = getopt(argc, argv, "ab:c")) != -1)
      {
        switch (option)
        {
        case 'a':
          Serial.println("Flag a detected");
          break;
        case 'b':
          Serial.print("option b detected, value:");
          Serial.println(optarg);
          break;
        case 'c':
          Serial.println("Flag c detected");
          break;
        default:     
         break;
        }
      }
    
      Serial.println("done");
    }
    
    void loop()
    {
    }
    Output:
    Code:
    option b detected, value: val
    : invalid option -- `-x'
    Flag c detected
    Flag a detected
    done

  13. #13
    Senior Member+ Theremingenieur's Avatar
    Join Date
    Feb 2014
    Location
    Colmar, France
    Posts
    2,066
    The question is for me „why this compulsive insisting on compiling code containing useless function calls“? The Teensy core files and libs are highly optimized and obviously don’t support functions which the embedded environment does not need, thus probably saving memory while some random Adafruit stuff does support it without improving its usability.

    My grandma could take her teeth off. But nobody would insist on everybody having removable teeth because of that... :-)

  14. #14
    Senior Member PaulStoffregen's Avatar
    Join Date
    Nov 2012
    Posts
    18,683
    What I want to know is why the C library implementation of getopt() is bringing in I/O stuff?

    I'm guessing this compile problem could be "fixed" by providing more dummy functions. But before doing that, I want to understand why.

  15. #15
    Senior Member
    Join Date
    Apr 2014
    Location
    Germany
    Posts
    307
    I can answer the first part of your question (not the second one...)

    It wants to output error messages: This
    Code:
    : invalid option -- `-x'
    was generated by getOpt().

    IIRC there is a compiler switch generating those dummy functions automatically. I can have a look if you really plan to support those functions. But again, why?

  16. #16
    Senior Member
    Join Date
    Feb 2017
    Posts
    191
    Since this is apparently the first time this particular issue has been raised, I'd say it's not worth pursuing. Thanks for looking.

  17. #17
    Senior Member
    Join Date
    Apr 2014
    Location
    Germany
    Posts
    307
    Just for fun: If you define _write() you can also use the normal printf().

    Code:
    #include "Arduino.h"
    #include "stdio.h"
    
    extern "C"
    { 
      int _write(int handle, char *buf, int count)
      {
        Serial.print("Written through _write: ");
        return Serial.write(buf, count);
      }
    }
    
    void setup()
    {
      Serial.begin(0);
      while (!Serial);
    
      printf("This is a test\n");    // <- no Serial.printf needed if _write is defined
    }
    
    void loop()
    {
    }
    Click image for larger version. 

Name:	printf.PNG 
Views:	1 
Size:	15.9 KB 
ID:	15261

  18. #18
    Senior Member+ Frank B's Avatar
    Join Date
    Apr 2014
    Location
    Germany NRW
    Posts
    4,768
    I was about to propose --specs=nosys.specs ,
    Then I noticed that adding a Serial.println("Hello world"); is enough to fix the error.

    _write is not needed. - Just add a print somewhere in your code.

    edit: But it does not print the errormessage ": invalid option -- `-x'"

  19. #19
    Senior Member+ Frank B's Avatar
    Join Date
    Apr 2014
    Location
    Germany NRW
    Posts
    4,768
    @gfvalvo we still don't know what this is good for? - and why you can't write your program without it (" Once that is solved, I can go on with the real code.")
    I think, it's solved now. Add a print() (or Luni's solution)

  20. #20
    Senior Member
    Join Date
    Apr 2014
    Location
    Germany
    Posts
    307
    edit: But it does not print the errormessage ": invalid option -- `-x'"
    In fact it looks like it crashes when it tries to print the error message...

  21. #21
    Senior Member+ Frank B's Avatar
    Join Date
    Apr 2014
    Location
    Germany NRW
    Posts
    4,768
    Oh, yes!. That's interesting.

  22. #22
    Senior Member
    Join Date
    Feb 2017
    Posts
    191
    Quote Originally Posted by Frank B View Post
    @gfvalvo we still don't know what this is good for?
    I merely asked about a compiler error that occurred while compiling for one board type and not another.

    - and why you can't write your program without it
    I don't recall saying that.

  23. #23
    Senior Member PaulStoffregen's Avatar
    Join Date
    Nov 2012
    Posts
    18,683
    Quote Originally Posted by luni View Post
    It wants to output error messages: This
    Code:
    : invalid option -- `-x'
    was generated by getOpt().
    Oh that's not good. Is this really what getopt() is supposed to do, print stuff to stdout? The Linux man page for getopt() seems to say it should just parse data.

  24. #24
    Senior Member
    Join Date
    Jul 2014
    Location
    New York
    Posts
    1,941
    Curiosity always seems to get the better of me. The only time i ever saw that _write error was when I tried to use printf without redefining it to Serial.printf. So i found the gcc++ getopt.h/.c online and it uses printf's in its function. So for for fun i change @luni's sketch to:
    Code:
    #include "Arduino.h"
    
    #define printf Serial.printf
    //#include <getopt.h>
    #include <unistd.h>
    /*
    extern "C"
    {
      // this is required for getopt to output error messages
      int _write(int handle, char *buf, int count)
      {   
        return Serial.write(buf, count);    
      }
    }
    */
    void setup()
    {
      Serial.begin(0);
      while (!Serial);
    
      char *argv[] = {new char[10], new char[10], new char[10],new char[10],new char[10]};
      int argc = 5;
    
      strcpy(argv[0],"");
      strcpy(argv[1],"-b val");
      strcpy(argv[2],"-x");
      strcpy(argv[3],"-c");
      strcpy(argv[4],"-a");
    
      int option;
      while ((option = getopt(argc, argv, "ab:c")) != -1)
      {
        switch (option)
        {
        case 'a':
          Serial.println("Flag a detected");
          break;
        case 'b':
          Serial.print("option b detected, value:");
          Serial.println(optarg);
          break;
        case 'c':
          Serial.println("Flag c detected");
          break;
        default:     
         break;
        }
      }
    
      Serial.println("done");
    }
    
    void loop()
    {
    }
    . It compiled without error and ran. But the only output I got was:
    Code:
    option b detected, value: val
    If I change unistd to #include <getopt.h> gives me the same output. However, if you change the getopt call to:
    Code:
    getopt(argc, argv, "ab:c::x"
    it will output correctly:

    Code:
    option b detected, value: val
    Flag c detected
    Flag a detected
    done
    Maybe this will help

    EDIT: looking at the test sample in getopt.c this is probably the way to go:
    Code:
    #include "Arduino.h"
    
    #define printf Serial.printf
    #include <getopt.h>
    
    void setup()
    {
      Serial.begin(0);
      while (!Serial);
    
      char *argv[] = {new char[10], new char[10], new char[10],new char[10],new char[10]};
      int argc = 5;
    
      strcpy(argv[0],"");
      strcpy(argv[1],"-b val");
      strcpy(argv[2],"-a");
      strcpy(argv[3],"-c");
      strcpy(argv[4],"-x");
    
      int option;
      while ((option = getopt(argc, argv, "ab:c::x")) != -1)
      {
        switch (option)
        {
        case 'a':
          Serial.println("Flag a detected");
          break;
        case 'b':
          Serial.print("option b detected, value:");
          Serial.println(optarg);
          break;
        case 'c':
          Serial.println("Flag c detected");
          break;
        case '?':
          break;
        default:
          printf ("?? getopt returned character code %c ??\n", option);     
         break;
        }
      }
    
      Serial.println("done");
    }
    
    void loop()
    {
    }
    which gives the following output:
    Code:
     option b detected, value: val
    Flag a detected
    Flag c detected
    ?? getopt returned character code x ??
    done
    Last edited by mjs513; 11-30-2018 at 01:30 PM.

  25. #25
    Senior Member
    Join Date
    Apr 2014
    Location
    Germany
    Posts
    307
    Just my personal opinion:

    I don't think it makes sense to "fix" anything here. If one wants to use any function from the standard library which needs to write / read from the standard streams in an embedded environment he necessarily has to tell libc how he intends to do that. Libc can not know if you want to display it on an attached display, some UART or printing the text on a 3d printer. This is exactly why libc requires_write and _read to be defined by the user in embedded systems.

    I also don't think it is a good idea do introduce dummy functions here. They will just make the linker error go away. E.g. if you have a dummy _write(). printf() would perfectly compile but wouldn't print. That would probably generate more questions from inexperienced users than having the linker error which shows them "printf doesn't work on this system". For experienced users it is no problem at all to provide implementations of _write / _read so they can use the library functions if they want.

    I suggest to leave everything as it is now because this seems to be the indented behavior, it doesn't confuse beginners and it is easy to make it run for experienced users.

    Just my thoughts...
    Last edited by luni; 11-30-2018 at 02:11 PM.

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •