tobozo / ESP32-targz

🗜️ An Arduino library to unpack/uncompress tar, gz, and tar.gz files on ESP32 and ESP8266
Other
123 stars 16 forks source link

multiple definition of `targzFreeBytesFn()' #39

Open sharandac opened 3 years ago

sharandac commented 3 years ago

Hey hey. I tried today to use ESP32-targz in several places in the same project. This results in the following error ...

Any Idea?

.pio/build/ttgo-t-watch-v1/src/utils/http_ota/http_ota.cpp.o: In function `targzFreeBytesFn()':
/home/sharan/Schreibtisch/My-TTGO-Watch/.pio/libdeps/ttgo-t-watch-v1/ESP32-targz/src/ESP32-targz.h:100: multiple definition of `targzFreeBytesFn()'
.pio/build/ttgo-t-watch-v1/src/app/watchface/watchface_app_tile.cpp.o:/home/sharan/Schreibtisch/My-TTGO-Watch/.pio/libdeps/ttgo-t-watch-v1/ESP32-targz/src/ESP32-targz.h:100: first defined here
.pio/build/ttgo-t-watch-v1/src/utils/http_ota/http_ota.cpp.o: In function `targzTotalBytesFn()':
http_ota.cpp:(.text._Z17targzTotalBytesFnv+0x0): multiple definition of `targzTotalBytesFn()'
.pio/build/ttgo-t-watch-v1/src/app/watchface/watchface_app_tile.cpp.o:watchface_app_tile.cpp:(.text._Z17targzTotalBytesFnv+0x0): first defined here
tobozo commented 3 years ago

hey thanks for your feedback :-)

just don't use setupFSCallbacks() with OTA Update and it should be fine

sharandac commented 3 years ago

Thank you for your answer. I have not yet used setFSCallbacks(). I have only used the following in two different places with

#define DEST_FS_USES_SPIFFS
#include <ESP32-targz.h>

Without any further code.

tobozo commented 3 years ago

two different places :thinking: maybe it doesn't like multiple instances ?

What does the targz setup code look like, can you share a snippet with all the statements calling this library ?

sharandac commented 3 years ago

Here is the first snippet with the function that use your lib

#define DEST_FS_USES_SPIFFS
#include <ESP32-targz.h>
/*
// some other code
*/
bool http_ota_start_compressed( const char* url, const char* md5, int32_t firmwaresize ) {
    bool retval = false;
    int32_t size = UPDATE_SIZE_UNKNOWN;

    HTTPClient http;
    /**
     * start get firmware file
     */
    http.setUserAgent( "ESP32-UPDATE-" __FIRMWARE__ );
    http.begin( url );
    int httpCode = http.GET();

    if ( httpCode > 0 && httpCode == HTTP_CODE_OK ) {
        /**
         * send http_ota_start event
         */
        http_ota_send_event_cb( HTTP_OTA_START, (void *)NULL );
        /**
         * start an unpacker instance, reister progress callback and put the stream in
         */
        GzUnpacker *GZUnpacker = new GzUnpacker();
        GZUnpacker->setGzProgressCallback( http_ota_progress_cb );
        GZUnpacker->setPsram( true );
        http_ota_send_event_cb( HTTP_OTA_START, (void*)"start flashing ..." );
        /**
         * if firmware size known set the right value
         */
        if ( firmwaresize != 0 )
            size = firmwaresize;
        /**
         * progress the stream
         */
        if( !GZUnpacker->gzStreamUpdater( http.getStreamPtr(), size, 0, false ) ) {
            log_e("gzStreamUpdater failed with return code #%d\n", GZUnpacker->tarGzGetError() );
            http_ota_send_event_cb( HTTP_OTA_ERROR, (void*)"error ... weak wifi?" );
        }
        else {
            http_ota_send_event_cb( HTTP_OTA_FINISH, (void*)"Flashing ... done!" );
            retval = true;
        }
    }
    else {
        http_ota_send_event_cb( HTTP_OTA_ERROR, (void*)"http error ... weak wifi?" );        
    }
    http.end();

    return( retval );
}

At this point it works perfect. But if I add in a second place, it fails. I have reduce it to only

#define DEST_FS_USES_SPIFFS
#include <ESP32-targz.h>
tobozo commented 3 years ago

interesting, this makes me realize there's no test suite for using gzStreamUpdater() with HTTP stream

Can you try to add this:

GZUnpacker->setupFSCallbacks( nullprt, nullptr );

then try to compile, if it still fails comment this out:

    // GZUnpacker->setGzProgressCallback( http_ota_progress_cb );

and see if it improves ?

Meanwhile I'll see if I can create an example sketch out of your snippet.

sharandac commented 3 years ago

With both changes comes the same compiler error. I am a bit confused, because actually the

#ifndef _TGZ_FSFOOLS_

should prevent this. But I don't see any reason why it does.

tobozo commented 3 years ago

idea: separate the gathering of the http stream pointer from the gz logic

Here's a function I use for that, it can even follow redirects:


#include <WiFi.h>
#include <HTTPClient.h>
#include <WiFiClientSecure.h>
HTTPClient http;

WiFiClient *getFirmwareClientPtr( WiFiClientSecure *client, const char* url, const char *cert = NULL )
{
  client->setCACert( cert );
  const char* UserAgent = "ESP32-HTTP-TarGzUpdater-Client";
  http.setReuse(true); // handle 301 redirects gracefully
  http.setUserAgent( UserAgent );
  http.setConnectTimeout( 10000 ); // 10s timeout = 10000
  if( ! http.begin(*client, url ) ) {
    log_e("Can't open url %s", url );
    return nullptr;
  }
  const char * headerKeys[] = {"location", "redirect", "Content-Type", "Content-Length", "Content-Disposition" };
  const size_t numberOfHeaders = 5;
  http.collectHeaders(headerKeys, numberOfHeaders);
  int httpCode = http.GET();
  // file found at server
  if (httpCode == HTTP_CODE_FOUND || httpCode == HTTP_CODE_MOVED_PERMANENTLY) {
    String newlocation = "";
    String headerLocation = http.header("location");
    String headerRedirect = http.header("redirect");
    if( headerLocation !="" ) {
      newlocation = headerLocation;
      Serial.printf("302 (location): %s => %s\n", url, headerLocation.c_str());
    } else if ( headerRedirect != "" ) {
      Serial.printf("301 (redirect): %s => %s\n", url, headerLocation.c_str());
      newlocation = headerRedirect;
    }
    http.end();
    if( newlocation != "" ) {
      log_w("Found 302/301 location header: %s", newlocation.c_str() );
      // delete client;
      return getFirmwareClientPtr( client, newlocation.c_str(), cert );
    } else {
      log_e("Empty redirect !!");
      return nullptr;
    }
  }
  if( httpCode != 200 ) return nullptr;
  return http.getStreamPtr();
}

So later I can do this:

  WiFiClientSecure *client = new WiFiClientSecure;
  Stream *streamptr = getFirmwareClientPtr( client, URL, certificate );
  Unpacker->gzStreamUpdater( streamptr, UPDATE_SIZE_UNKNOWN, 0, false );

also I don't include <ESP32-targz> from multiple places, maybe gather all unpacking logic into a single document for simplicity ?

sharandac commented 3 years ago

That will probably be the solution, that I will pack the functions into a file. Many thanks for your help!

tobozo commented 3 years ago

I've updated the example with a gzStreamUpdater test:

https://github.com/tobozo/ESP32-targz/tree/1.0.5-beta/examples/ESP32/Update_from_gz_stream

I don't see a compilation error but I've only tested with Arduino IDE, does it compile in your platformio environment ?

tibotix commented 3 years ago

Hey,

only wanted to mention, that it can be also resolved simply by putting the declarations of targzFreeBytesFn and targzTotalBytesFn in the ESP32-targz-lib.h file, and the definitions in the ESP32-targz-lib.cpp file. The FSInfo fsinfo declaration should also be moved to the ESP32-targz-lib.h header so it can be used by the targzFreeBytesFn function.

At least this worked for me :-)

tobozo commented 3 years ago

@tibotix doing this breaks esp8266 compatibility, but I'm not sure I'll keep on maintaining the esp8266 part of the library so I'll keep your suggestion in mind

tibotix commented 3 years ago

@tobozo oh okay I see. That was not intended.

Anyway, for others that might face this issue: if you have to include the ESP32-targz.h header multiple times, you could define the tarGzFS and FS_NAME Macros as well as the targzFreeBytesFn and targzTotalBytesFn functions and if needed the FSInfo fsinfo variable yourself and include the ESP32-targz-lib.h directly instead of the ESP32-targz.h header.

monte-monte commented 2 years ago

Strange thing is thatI have included ESP32-targz.h only once, but still have this error. I ended up declaring targzFreeBytesFn and targzTotalBytesFn locally.

tobozo commented 2 years ago

@monte-monte this was solved a year ago, what core and library versions are you using?

monte-monte commented 2 years ago

@tobozo version=1.1.4, esp8266 with latest core in platformio.