Compile Error Trying to Use getopt()

Status
Not open for further replies.

gfvalvo

Well-known member
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.
 
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.
 
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.
 
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:
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.
 
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.
 
Paul, Thierry, Frank,
he only did what the forum rules requires (code that reproduces the problem)
 
Just out of curiosity I tested the code posted by @luni. It compiles and runs but no output.
 
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!
 
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
 
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... :)
 
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.
 
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?
 
Since this is apparently the first time this particular issue has been raised, I'd say it's not worth pursuing. Thanks for looking.
 
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()
{
}

printf.PNG
 
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'"
 
@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)
 
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.
 
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:
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:
Status
Not open for further replies.
Back
Top