Šta je novo?

YAJL - Bildovanje sa CMake i MinGW ne prolazi

MasterChief2

Cenjen
Učlanjen(a)
01.03.2015
Poruke
860
Poena
115
Pozdrav svima. Ljudi koji prate Kodi temu verovatno vec znaju da vec neki vremenski period radim na XBMC4Xbox projektu. Za one koji ne znaju negde od februara krenuo sam da portujem feature sa novijih verzija Kodi programa na XBMC4Xbox. XBMC4Xbox je fork Kodi-ja namenjen Xbox konzolama prve generacije.

Trenutno sam zakucan (par dana vec) na dodavanju JSON biblioteke koja je neophodna za neke feature koje portujem. U pitanje je ova biblioteka, zove se YAJL. Ono sto ja zelim jeste da ovu biblioteku dodam u XBMC projekat kako bi mogao da bekportujem JSONVariantParser i JSONVariantWriter klase. E sad stvari se malo komlikuju jer je u pitanju Xbox koji pokrece osakacenu verziju Windows-a 2000 koji nema nikakvu podrsku za DLL/LIB fajlove. XBMC programeri su to nadomestili kreiranje DllLoader klase koja prosiruje kernel modul sa nedostajecim funkcijama tako da se DLL fajlovi biblioteka mogu ucitati i koristiti. Uspeo sam da stupim u kontakt sa prethodnim programerom koji je razvijao XBMC4Xbox u proslosti i on mi je dao sledece smernice:
1. Koja god biblioteka da je u pitanju, u 99% slucaja se bilduje sa MinGW kompajlerom. Dakle kao kada zelite da bildujete neku biblioteku za Windows 2K/XP, procedura je ovde sasvim ista
2. Bildovanje je najlakse odraditi koriscenjem CMake alata (sa kojim ja nikada nisam imao kontakta do sada)
3. Rezultat bildovanja je Dynamic Library (DLL), Static Library (LIB) ili kombinacija ova dva
4. Nakon toga potrebno je napisati DLL wrapper klasu koja nasledjuje DllLoader kalsu i definise sve exportovane metode iz DLL-a (njihova povratna vrednost, njihova deklaracija i njihovi ulazni parametri)

U teoriji nista specijalno kada je u pitanju neka jednostavnija biblioteka kao sto je JSON, medjutim u praksi malo je drugacije prica. Otprilike sam shvatio kako se pise DLL wrapper klasa, medjutim mene muci to sto nikako ne mogu da bildujem YAJL sa CMake i MinGW. Uvek dobijam neke greske. Koraci koja sam radio su sledeci:
  1. klonirao repozitorijum sa github-a
  2. kreirao build folder i prebacio se u njega sa:
    1. Bash:
      mkdir build && cd build
  3. i pokrenu komandu za kreranej fajlova potrebnih za build sa:
    1. Bash:
      cmake -G "MinGW Makefiles" ..
  4. I nakon toga dobijam ovu gresku:
C:\Documents and Settings\Nikola Antonic\Desktop\yajl-master\build>cmake -G "Min
GW Makefiles" ..
-- The C compiler identification is GNU 6.3.0
-- Check for working C compiler: C:/MinGW/bin/gcc.exe
-- Check for working C compiler: C:/MinGW/bin/gcc.exe -- works
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Detecting C compile features
-- Detecting C compile features - done
Error: Target (for copy_if_different command) "Antonic/Desktop/yajl-master/build
/src/../yajl-2.1.1/include/yajl" is not a directory.
Error: Target (for copy_if_different command) "Antonic/Desktop/yajl-master/build
/src/../yajl-2.1.1/include/yajl" is not a directory.
Error: Target (for copy_if_different command) "Antonic/Desktop/yajl-master/build
/src/../yajl-2.1.1/include/yajl" is not a directory.
Error: Target (for copy_if_different command) "Antonic/Desktop/yajl-master/build
/src/../yajl-2.1.1/include/yajl" is not a directory.
CMake Warning (dev) at reformatter/CMakeLists.txt:38 (GET_TARGET_PROPERTY):
Policy CMP0026 is not set: Disallow use of the LOCATION target property.
Run "cmake --help-policy CMP0026" for policy details. Use the cmake_policy
command to set the policy and suppress this warning.

The LOCATION property should not be read from target "json_reformat". Use
the target name directly with add_custom_command, or use the generator
expression $<TARGET_FILE>, as appropriate.

This warning is for project developers. Use -Wno-dev to suppress it.

CMake Warning (dev) at verify/CMakeLists.txt:32 (GET_TARGET_PROPERTY):
Policy CMP0026 is not set: Disallow use of the LOCATION target property.
Run "cmake --help-policy CMP0026" for policy details. Use the cmake_policy
command to set the policy and suppress this warning.

The LOCATION property should not be read from target "json_verify". Use
the target name directly with add_custom_command, or use the generator
expression $<TARGET_FILE>, as appropriate.

This warning is for project developers. Use -Wno-dev to suppress it.

!! doxygen not found, not generating documentation
-- Configuring done
-- Generating done
-- Build files have been written to: C:/Documents and Settings/Nikola Antonic/De
sktop/yajl-master/build

C:\Documents and Settings\Nikola Antonic\Desktop\yajl-master\build>

Da li ima neko na forumu ko se razume u ovo, pre svega ko je koristio CMake za bildovanje projekata jer ja nisam nikada, tako da ne znam ni gde da trazim problem.. Napomenuo bih da sam probao i sa drugim opcijama za -G kao sto su koriscenje Visual Studio 2010 medjutim isti problem imam. Nije do lokalnog MinGW i CMake jer sam uspeo da generisem DLL za jsoncpp biblioteku.

Unapred hvala!
 
Uspeo sam "kao" da kompajliram. Bilo je potrebno izbrisati neke stvari kako bi MinGW mogao da kompajlira uspesno. Na slici sam okacio sta sam uzmenio u CMakeLists.txt fajlu da bi prosao build. Medjutim kompajlirani DLL je ili los ili neki DLL fali na Windowsu jer kada pozovem yajl_alloc metodu dobijam gresku sa slike 2. Kada kompajliram sa Visual Studio 2010, mogu da ucitam DLL i pozovem yajl_alloc metodu unutar pythona. Dakle nesto radim lose, samo je pitanje sta. Probao sam da ubacim DLL u XBMC medjutim cim pozovem to metodu odma dobijam "illegal exception at address 0x...."

Mislim da mi najpametnije bilo ako bi uspeo da napravim neki svoj dummy DLL fajl. Najjednostavniji i sto prostiji da bude. Da exportuje par funkcija i klasa i da znam da radi. Pa da onda vidim kako da ga ucitam na Xbox-u, tj. u XBMC-u pa kada to sve prodje uspesno da se onda igram sa slozenijim bibliotekama. Pitam se samo koji je mozak uspeo u sve ovo da ubaci Python, CURL, PolarSSL i ostale biblioteke..
 

Prilozi

  • Screenshot_20230812_215732.png
    Screenshot_20230812_215732.png
    139.8 KB · Pregleda: 17
  • Screenshot_20230812_220149.png
    Screenshot_20230812_220149.png
    80.1 KB · Pregleda: 17
Proveri da li ti je generisan 64-bitni fajl umesto 32-bitni. Zatim proveri da li su svi DLL exporti na mestu. (Mada to treba da ti je i u xbmc debug logu)

U vezi sa CMakeLists, ja ne vidim šta je tu problematično. Apropo (N)DEBUG, taj deo nije tačan jer će obe macro definicije biti prisutne istovremeno.
Kod:
string(TOLOWER "${CMAKE_BUILD_TYPE}" CMAKE_BUILD_TYPE)
if (CMAKE_BUILD_TYPE STREQUAL "debug")
  add_definitions(-DDEBUG)
elseif (CMAKE_BUILD_TYPE STREQUAL "release")
  add_definitions(-DNDEBUG)
endif ()
# ili za novije CMake verzije:
add_compile_definitions(
        $<$<CONFIG:RELEASE>:NDEBUG>
        $<$<CONFIG:RELWITHDEBINFO>:NDEBUG>
        $<$<CONFIG:MINSIZEREL>:NDEBUG>
        $<$<CONFIG:DEBUG>:DEBUG>
)

Da dodam još po nešto:
  • obrati pažnju na CMakeCache koji može stvarati pogrešne probleme ako nije ažuran
  • na brzi pogled deluje mi da treći argument u yajl_alloc ne sme biti nullptr i da ti zato xbmc krešuje
  • obrati pažnju na calling conventions. Kompajler može da se odluči za stdcall umesto cdecl proizvoljno (vid optimizacije) ako nisu specifikovao atribute kako treba (tj. imperativno). Ista stvar kad pozivaš eksterni kod. Pogotovo ako se kompajleri razlikuju.
---
Na stranu problem sa yajl-om, tebi je potrebna JSON biblioteka samo unutar XBMC C++ koda, ili? Ako je to u pitanju, bolje ti je da uzmeš neku header-only biblioteku poput nlohmann/json i samo odradiš #include. Za Python imaš ugrađen json modul. Eventualno uradiš wrapper da bude 1:1 sa C++ kodom.
(Plus što se drugima verovatno neće svideti nepoznati DLL ako baš nije potreban)
 
Proveri da li ti je generisan 64-bitni fajl umesto 32-bitni. Zatim proveri da li su svi DLL exporti na mestu. (Mada to treba da ti je i u xbmc debug logu)
Kako to da proverim? Mada ako bildujem sa CMake, trebalo bi da sam prepozna da je WIN32? U xmbc.log-u takve stvari ne pisu. Okacio sam log fajl pa mozes i sam baciti pogled :)
U vezi sa CMakeLists, ja ne vidim šta je tu problematično. Apropo (N)DEBUG, taj deo nije tačan jer će obe macro definicije biti prisutne istovremeno.
Kod:
string(TOLOWER "${CMAKE_BUILD_TYPE}" CMAKE_BUILD_TYPE)
if (CMAKE_BUILD_TYPE STREQUAL "debug")
  add_definitions(-DDEBUG)
elseif (CMAKE_BUILD_TYPE STREQUAL "release")
  add_definitions(-DNDEBUG)
endif ()
# ili za novije CMake verzije:
add_compile_definitions(
        $<$<CONFIG:RELEASE>:NDEBUG>
        $<$<CONFIG:RELWITHDEBINFO>:NDEBUG>
        $<$<CONFIG:MINSIZEREL>:NDEBUG>
        $<$<CONFIG:DEBUG>:DEBUG>
)
Iz ovog primera se vidi da nemam pojma CMake i radim stvari napamet :) Hvala na ucenoj gresci!
Da dodam još po nešto:
  • obrati pažnju na CMakeCache koji može stvarati pogrešne probleme ako nije ažuran
  • na brzi pogled deluje mi da treći argument u yajl_alloc ne sme biti nullptr i da ti zato xbmc krešuje
Mislim da su male sanse da je do toga. Kao treci parametar u XBMC-u se prosledjuje "this". Sto mu dodje instance te klase. Pre ce biti da DLL ne valja i da zbog toga baca gresku. Tj. mislim da kod puca negde u tom DllLoaderu. Ili ja pogresno definisem funkcije jer puca koju kod funkciju da pozovem iz DLL-a.
Na stranu problem sa yajl-om, tebi je potrebna JSON biblioteka samo unutar XBMC C++ koda, ili? Ako je to u pitanju, bolje ti je da uzmeš neku header-only biblioteku poput nlohmann/json i samo odradiš #include.
U sustini ja pokusavam da backportujem ove commitove:
  1. Added yajl writer for Variant
  2. Added yajl parser for Variant
Biblioteka mi nije bitna, ali ako bi koristio neku drugu onda bi morao i da kreiram ove klase od nule (ili samo ove delove koji koriste yajl). Uspeo sam jsoncpp biblioteku da ubacim u projekat, sad cu videti da li mogu nekako da uradim rewrite ovih klasa. Ono sto mene najvise muci je to sto XDK koristi C++98 standard, a u danasnje vreme skoro i da nema novih biliboteka koje podrzavaju toliko matoru verziju.

Za Python imaš ugrađen json modul. Eventualno uradiš wrapper da bude 1:1 sa C++ kodom.
(Plus što se drugima verovatno neće svideti nepoznati DLL ako baš nije potreban)
Python koristim samo radi testiranja. Hteo sam da vidim da li taj DLL moze da se ucita i koristi, pa da se tek onda bacim na XBMC u kom apsolutno nemam predstavu kako se DLL u praksi ucitava osim hintova koje mi je dao BuZz (glavni i odgovorni za XBMC4Xbox). Prvo sam hteo da napravim konzolnu aplikaciju u VS2003 i da u taj projekat ubacim DLL, ali nisam nigde nasao info kako se to radi, a u VS2010 je skroz drugacije pa sam se zbog toga odlucio za Python.

Da li znas mozda kako napraviti neki dummy DLL za potrebe testiranja? Ako bi uspeo taj DLL da ucitam i pozovem funkciju koja vraca "Hello World" onda bi mogao da pokusam sa nekom vecom bibliotekom. Ovako samo mogu nagadjati jer problem moze biti na bilo kom mestu (lose kompajliran DLL, mozda treba nesto pecovati u source code da bi radio na Xbox-u, lose ucitavanje u DLL-a u XBMC-u itd.)
 
Kako to da proverim? Mada ako bildujem sa CMake, trebalo bi da sam prepozna da je WIN32?
Win32 označava da je sistem koji builduje Windows, nebitno da li 32/64-bitni. Ono što tebi treba je compiler flag koji kaže da target mora biti 32-bitni, a ja to ne vidim u CMakeLists fajlu na upstream-u. Na GCC-u je to -m32, ne znam za MSVC/MinGW.

Ne znam najbolji/najprostiji način, ali možeš da proveriš uz neku alatku poput Dependency Viewer, CFF Explorer, PEBear..
U xmbc.log-u takve stvari ne pisu.
Potrebna je DUMPING_DATA definicija prilikom kompajliranja.
Kao treci parametar u XBMC-u se prosledjuje "this". Sto mu dodje instance te klase.
Ta biblioteka koja se u ta dva komita nalazi prima 4 argumenta za yajl_alloc, dok biblioteka koju si okačio gore uzima svega 3. Nisam zagledao koji je parametar nestao.
Biblioteka mi nije bitna, ali ako bi koristio neku drugu onda bi morao i da kreiram ove klase od nule (ili samo ove delove koji koriste yajl). Uspeo sam jsoncpp biblioteku da ubacim u projekat, sad cu videti da li mogu nekako da uradim rewrite ovih klasa.
Zapravo ti samo treba da napišeš dve metode: Parse (učitava json u CVariant) i Write (ispisuje CVariant u JSON string). Što je krajnje prosto uz malo rekurzije (+ dodatna "pivot" funkcija koja će vršiti rekurziju).
Ono sto mene najvise muci je to sto XDK koristi C++98 standard, a u danasnje vreme skoro i da nema novih biliboteka koje podrzavaju toliko matoru verziju.
C++11, C++14, itd. su nadskupovi C++98, tako da ne bi trebalo da imaš problema. Uglavnom se svodi na to da proslediš -std=c++11 argument kompajleru, mada mislim da ni ne moraš više i da je to standard kod novijih kompajlera. Eventualno -Wno-error.
Python koristim samo radi testiranja. Hteo sam da vidim da li taj DLL moze da se ucita i koristi, pa da se tek onda bacim na XBMC u kom apsolutno nemam predstavu kako se DLL u praksi ucitava osim hintova koje mi je dao BuZz (glavni i odgovorni za XBMC4Xbox). Prvo sam hteo da napravim konzolnu aplikaciju u VS2003 i da u taj projekat ubacim DLL, ali nisam nigde nasao info kako se to radi, a u VS2010 je skroz drugacije pa sam se zbog toga odlucio za Python.
To što ti treba se svodi na: LoadLibrary + GetProcAddress i to je to. Evo primera: https://blog.benoitblanchon.fr/getprocaddress-like-a-boss/ i https://goffconcepts.com/techarticles/calldll.html
WINAPI definicija ovde ti je __stdcall, ali tebi će najverovatnije trebati __cdecl.
Da li znas mozda kako napraviti neki dummy DLL za potrebe testiranja? Ako bi uspeo taj DLL da ucitam i pozovem funkciju koja vraca "Hello World" onda bi mogao da pokusam sa nekom vecom bibliotekom. Ovako samo mogu nagadjati jer problem moze biti na bilo kom mestu (lose kompajliran DLL, mozda treba nesto pecovati u source code da bi radio na Xbox-u, lose ucitavanje u DLL-a u XBMC-u itd.)
Mislim da se u VS-u to zove samo "Dynamic Library" kod onih template-ova. Tražiš onaj koji će ti dati generisan DllMain stub. DllMain ti neće trebati (to ti je callback kada se DLL load/unload-uje). I tu bi eventualno mogao da dodaš neki file logging (najprostiji std::eek:fstream sa append flagom) čisto da vidiš kada se učitava DLL. Funkcije za export ćeš morati da označiš sa: extern "C" __declspec(dllexport).

PS. Zaboravio sam da ti kažem da imaš još jednu opciju: da staviš yajl kao podfolder u XBMC i iz (nekog) lokalnog Makefile-a (iz XBMC-a, ne iz yajl-a) samo dodaš .c fajlove u SRCS direktivu (npr. yajl/yajl.c yajl/yajl_gen.c itd...). Include path neće biti najlepši, al' će da radi pos'o.

[Da XBMC koristi CMake, umesto običan Makefiles, mogao bi samo da odradiš add_subdirectory i eventualno target_include_directories da dodaš taj direktorijum u system includes (tj. include sa <>, umesto "").]
 
Ta biblioteka koja se u ta dva komita nalazi prima 4 argumenta za yajl_alloc, dok biblioteka koju si okačio gore uzima svega 3. Nisam zagledao koji je parametar nestao.
Postoje dve verzije YAJL bilbioteke. Verzija 1.x i verzija 2.x. Ovi komitovi su pisani za verziju 1.x, a ovaj commit je dodao podrsku za verziju 2. Otuda i razlika u broju parametera.
Zapravo ti samo treba da napišeš dve metode: Parse (učitava json u CVariant) i Write (ispisuje CVariant u JSON string). Što je krajnje prosto uz malo rekurzije (+ dodatna "pivot" funkcija koja će vršiti rekurziju).
Upravo sam to uradio. Kada sam malo bolje pogledao dosao sam i sam do tog zakljucka. Na kraju sam se odlucio za jsoncpp biblioteku. Klonirao sam granu koja je kompatibilna sa C++98 i pokrenuo python scriptu python amalgamate.py. Koliko sam shvatio ova skripta sav kod nabaca u jedan .cpp i jedan .h fajl. Onda sam samo to dodao u XBMC projekat i za sada radi. Uspeo sam da reimplemenitram obe klase da koriste jsoncpp umesto YAJL-a i za sada radi kako treba. Barem ovaj kod koji zavisi od tih klasa radi kako treba, tj. konacno su mi SmartPlayliste proradile. Videcu hoce li biti nekih problema sa ovim klasama kada objavim prvu Betu. Posto vidim da se poprilicno razumes u ove stvari, ako ti nije mrsko baci pogled na ove dve klase (nisu velike). Najvise me zanima da nisam negde slucajno koji mem leak napravio, mada ne bi trebalo.
C++11, C++14, itd. su nadskupovi C++98, tako da ne bi trebalo da imaš problema. Uglavnom se svodi na to da proslediš -std=c++11 argument kompajleru, mada mislim da ni ne moraš više i da je to standard kod novijih kompajlera. Eventualno -Wno-error.
Imao sam i to dosta nazalost. Neke sintakse nisu podrzane u C++98 pa sam moram da trazim alternative (npr. auto priliko iteriranja kroz vector). std biblioteka nije imala neke funkcije koje su mi trebale, mislim da je jedna od njih bila std::function. U sustini neke delove koda sam morao da menjam jer C++98 nije ima te stvari ugradjene.
To što ti treba se svodi na: LoadLibrary + GetProcAddress i to je to. Evo primera: https://blog.benoitblanchon.fr/getprocaddress-like-a-boss/ i https://goffconcepts.com/techarticles/calldll.html
WINAPI definicija ovde ti je __stdcall, ali tebi će najverovatnije trebati __cdecl.
Bacicu pogled na ovo cim uhvatim vremena. Posto cemi ucitavanje DLL fajla u XBMC-u trebati zasigurno u buducnosti.
Mislim da se u VS-u to zove samo "Dynamic Library" kod onih template-ova. Tražiš onaj koji će ti dati generisan DllMain stub. DllMain ti neće trebati (to ti je callback kada se DLL load/unload-uje). I tu bi eventualno mogao da dodaš neki file logging (najprostiji std::eek:fstream sa append flagom) čisto da vidiš kada se učitava DLL. Funkcije za export ćeš morati da označiš sa: extern "C" __declspec(dllexport).
Hvala, pogledacu!

Sve bi ovo islo lakse da ja znam C/C++. Ovako sam ih radio poslednji put na FTN-u u drugoj godini i to neke budalastine koje sam znao jos iz drugog srednje. Od druge godine pa sve do sada nisam imao kontakta sa ovakvim vidom programiranja. U firmi radim web, na fakultetu sam radio isto samo web, a slozices se da je web programiranje smejurija naspram ovoga. Al polako, od februara daleko sam dogurao sa XBMC-em, tj. uspeo sam da poteram Estuary skin sto je i bio glavni cilj.
 

Prilozi

  • JSONVariantParser.cpp.txt
    2.8 KB · Pregleda: 2
  • JSONVariantParser.h.txt
    1.7 KB · Pregleda: 0
  • JSONVariantWriter.cpp.txt
    2.6 KB · Pregleda: 2
  • JSONVariantWriter.h.txt
    1.1 KB · Pregleda: 0
ako ti nije mrsko baci pogled na ove dve klase (nisu velike). Najvise me zanima da nisam negde slucajno koji mem leak napravio, mada ne bi trebalo.
Izgleda sasvim ok. Možda bih dve stvari promenio: u CJSONVariantWriter::Write, InternalWrite grananje bih stavio u try/catch block ako jsoncpp izbaci neki exception (InternalWrite može da bude i void, jer false nikad neće vratiti). Drugu stvar koju bih izmenio je u CJSONVariantParser::ConvertJsonValueToCVariant i dodao bih log key-eva u slučaju gde vraća obični CVariant().
Imao sam i to dosta nazalost. Neke sintakse nisu podrzane u C++98 pa sam moram da trazim alternative (npr. auto priliko iteriranja kroz vector). std biblioteka nije imala neke funkcije koje su mi trebale, mislim da je jedna od njih bila std::function. U sustini neke delove koda sam morao da menjam jer C++98 nije ima te stvari ugradjene.
Mislio sam kada se C++98 kompajlira sa C++11 kompajlerom, ne obrnuto. U ovoj "apgrejd" varijanti jedino mi pada na pamet da možda neke kvalifikacije za virtuelne funkcije/varijable neće raditi kako treba, tj. da je C++11 malo stroži po tom pitanju.
 
Izgleda sasvim ok. Možda bih dve stvari promenio: u CJSONVariantWriter::Write, InternalWrite grananje bih stavio u try/catch block ako jsoncpp izbaci neki exception (InternalWrite može da bude i void, jer false nikad neće vratiti). Drugu stvar koju bih izmenio je u CJSONVariantParser::ConvertJsonValueToCVariant i dodao bih log key-eva u slučaju gde vraća obični CVariant().
Hvala, ispravljeno.
Mislio sam kada se C++98 kompajlira sa C++11 kompajlerom, ne obrnuto. U ovoj "apgrejd" varijanti jedino mi pada na pamet da možda neke kvalifikacije za virtuelne funkcije/varijable neće raditi kako treba, tj. da je C++11 malo stroži po tom pitanju.
Za sada se nekako snalazim, ali sto vise idem ka novijim verzijama Kodija sve je veci problem. Kada portujem i uradim sve sto imam u planu i softver postane dovoljno stabilan, sledeci korak ce biti definitivno portovanje XBMC upotrebom nxdk-a, a ne ovog matorog XDK-a. On podrzava sve najnovije standarde i nadam se da ce u skorije vreme uspeti da integrisu OpenGL.
 
Nazad
Vrh Dno