Je třetí sobota v září a proto vše nejlepší k dnešnímu Software Freedom Day (SFD, Wikipedie).
Bogdan Ionescu rozběhl webový server na jednorázové elektronické cigaretě.
Byla vydána beta verze Ubuntu 25.10 s kódovým názvem Questing Quokka. Přehled novinek v poznámkách k vydání. Dle plánu by Ubuntu 25.10 mělo vyjít 9. října 2025.
Bola vydaná nová verzia 4.13 security platformy Wazuh. Prináša nový IT hygiene dashboard, hot reload dekodérov a pravidiel. Podrobnosti v poznámkách k vydaniu.
Americký výrobce čipů Nvidia investuje pět miliard dolarů (přes 100 miliard Kč) do konkurenta Intel, který se v poslední době potýká s vážnými problémy. Firmy to včera oznámily ve společné tiskové zprávě. Dohoda o investici zahrnuje spolupráci při vývoji čipů pro osobní počítače a datová centra. Akcie společnosti Intel na zprávu reagovaly výrazným růstem.
Dlouholetý balíčkář KDE Jonathan Riddell končí. Jeho práci na KDE neon financovala firma Blue Systems, která ale končí (Clemens Tönnies, Jr., dědic jatek Tönnies Holding, ji už nebude sponzorovat), někteří vývojáři KDE se přesunuli k nově založené firmě Techpaladin. Pro Riddella se již nenašlo místo. Následovala debata o organizaci těchto firem, které zahraniční vývojáře nezaměstnávají, nýbrž najímají jako kontraktory (s příslušnými důsledky z pohledu pracovního práva).
V Amsterdamu probíhá Blender Conference 2025. Videozáznamy přednášek lze zhlédnout na YouTube. V úvodní keynote Ton Roosendaal oznámil, že k 1. lednu 2026 skončí jako chairman a CEO Blender Foundation. Tyto role převezme současný COO Blender Foundation Francesco Siddi.
The Document Foundation, organizace zastřešující projekt LibreOffice a další aktivity, zveřejnila výroční zprávu za rok 2024.
Byla vydána nová stabilní verze 7.6 webového prohlížeče Vivaldi (Wikipedie). Postavena je na Chromiu 140. Přehled novinek i s náhledy v příspěvku na blogu.
Byla vydána verze 1.90.0 programovacího jazyka Rust (Wikipedie). Podrobnosti v poznámkách k vydání. Vyzkoušet Rust lze například na stránce Rust by Example.
V práci jsme pro QA oddělení nakódili python plugin pro náš software, aby se už chudáci testeři nemuseli mořit s testováním na základě logů, do kterých jsou vyexportovány všechny data jako ASCII (takový log má klidně přes 100GB zagzippovaný, brr). Tento plugin se osvědčil, ale co si budeme povídat, CPython je pro zpracování velkého množství dat pomalý (a s LuaJIT mě šéf poslal někam ). Takže zkusíme embedovat PyPy ... no, byla to sranda už jen díky tomu, že jediná "dokumentace" jsou zdrojáky uwsgi PyPy pluginu. Naštěstí se nakonec ukázalo, že embedování PyPy je celkem fajn a následující řádky by měly posloužit jako dokumentace embedování PyPy do C (resp. C++) aplikací.
Ouch, bohužel PyPy v dnešních distribucích není zkompilováno s "--shared", takže je potřeba překompilovat. Naštěstí poslední oficiální release "--shared" podporuje, takže není potřeba řešit mercurial, ani žádné (pochybné) patche .
wget https://bitbucket.org/pypy/pypy/downloads/pypy-2.2.1-src.tar.bz2 tar xf pypy-2.2.1-src.tar.bz2 cd pypy-2.2.1 # Pozor, samotná kompilace PyPy vyžaduje PyPy (takže "aptitude install pypy", resp. alternativa pro vaší distribuci) ./rpython/bin/rpython -Ojit --shared --gcrootfinder=shadowstack pypy/goal/targetpypystandalone
int main(int argc, char *argv[]) { rpython_startup_code(); pypy_execute_source("print('Hello World')\n"); pypy_init_threads(); return 0; }Stačí zkompilovat a spustit:
gcc prvni_priklad.c -Lcesta_k_pypy -lpypy-c -o prvni_priklad LD_LIBRARY_PATH=cesta_k_pypy ./prvni_priklad
Ve verzích >2.2.1 už PyPy.h bude, takže nebude potřeba takto stahovat.
Pokud zkusíte překompilovat prvni_priklad.c pomocí g++, kompilace skončí s chybou (gcc -Wall také vypíše ekvivalentní warning), samozřejmě, funkce rpython_startup_code a pypy_execute_source nejsou deklarovány.
Jak jsem v perexu zmiňoval, embedování PyPy je težce cutting-edge a v posledním stable vydání zatím není PyPy.h (ten byl přidán commitem c4cd6ec).
Stačí tedy stáhnout PyPy.h z repositáře PyPy a umístit ho do adresáře include/
cd cesta_k_pypy/include wget https://bitbucket.org/pypy/pypy/raw/c4cd6eca9358066571500ac82aaacfdaa3889e8c/include/PyPy.h
Přidejte #include <PyPy.h> na začátek prvni_priklad.c a můžete překompilovat s g++
g++ -Icesta_k_pypy/include -Wno-write-strings prvni_priklad.c -Lcesta_k_pypy -lpypy-c -o prvni_priklad LD_LIBRARY_PATH=cesta_k_pypy ./prvni_priklad
Příklad "Hello World" sice funguje hezky, ale pro reálné využití zbývá vyřešit následující:
Dále ukážu můj boilerplate pro první dva problémy, CFFI je kapitola sama pro sebe.
Moje řešení je trochu složitější a navíc je GNU (glibc) a Linux specifické, proto by se na něj neměl nikdo dívat.
Tady by bylo lepší alternativou použít build systém k vygenerování a hardkódování cesty k .py souboru.
#include <string.h> #include <stdlib.h> #include <PyPy.h> #include <libgen.h> #include <stdio.h> // caller should free returned pointer to avoid memleaks char* change_basename(char* full_path,char* new_basename) { char* full_path_copy = strdup(full_path); char* dirpath = dirname(full_path_copy); size_t new_path_len = strlen(dirpath)+1+strlen(new_basename); // +1 because of "/" char* new_path = (char*)malloc( new_path_len+1 ); // +1 because of terminating NULL memset(new_path, 0, new_path_len+1); strcat(new_path, dirpath); strcat(new_path, "/"); strcat(new_path, new_basename); free(full_path_copy); return new_path; } // caller should free returned pointer to avoid memleaks // returns NULL on error char* get_path_to_pyfile() { // Linux only // http://stackoverflow.com/questions/1023306/finding-current-executables-path-without-proc-self-exe // http://stackoverflow.com/questions/5919996/how-to-detect-reliably-mac-os-x-ios-linux-windows-in-c-preprocessor char* buf = realpath("/proc/self/exe",NULL); if (buf == NULL) { return NULL; } char* py_file = change_basename(buf,"setup.py"); free(buf); return py_file; } // caller should free returned pointer to avoid memleaks // returns NULL on error char* load_python_source(char* filepath) { char *buffer; long input_file_size; FILE *input_file = fopen(filepath, "rb"); if (input_file == NULL) { return NULL; } fseek(input_file, 0, SEEK_END); input_file_size = ftell(input_file); rewind(input_file); buffer = (char*)malloc(input_file_size * (sizeof(char))+1); memset(buffer, 0, input_file_size* (sizeof(char))+1); fread(buffer, sizeof(char), input_file_size, input_file); fclose(input_file); return buffer; } int main(int argc, char *argv[]) { // PyPy initialization char* py_file = get_path_to_pyfile(); if (!py_file) { printf("Error determining path to the python setup file (setup.py)\n"); exit(1); } char* buffer = load_python_source(py_file); if (!buffer) { printf("Error opening python setup file (setup.py)\n"); exit(1); } // actual pypy initialization rpython_startup_code(); pypy_execute_source( buffer ); pypy_init_threads(); // free stuff if (py_file != NULL) { free(py_file); } if (buffer != NULL) { free(buffer); } // end of PyPy initialization return 0; }
Aby správně fungoval import, je potřeba před zavoláním funkce pypy_execute_source zavolat pypy_setup_home.
Funkci pypy_setup_home je potřeba předat cestu k lib_pypy. Nenašel jsem způsob jak tuto cestu najít čistým způsobem (např. pomocí pkg-config) a proto následující glibc specifický hack.
#if !(_GNU_SOURCE) #define _GNU_SOURCE #endif #include <stdio.h> #include <dlfcn.h> #include <unistd.h> #include <string.h> #include <stdlib.h> #include <libgen.h> #include <PyPy.h> // caller should free returned pointer to avoid memleaks char* change_basename(char* full_path,char* new_basename) { char* full_path_copy = strdup(full_path); char* dirpath = dirname(full_path_copy); size_t new_path_len = strlen(dirpath)+1+strlen(new_basename); // +1 because of "/" char* new_path = (char*)malloc( new_path_len+1 ); // +1 because of terminating NULL memset(new_path, 0, new_path_len+1); strcat(new_path, dirpath); strcat(new_path, "/"); strcat(new_path, new_basename); free(full_path_copy); return new_path; } // caller should free returned pointer to avoid memleaks // returns NULL on error char* guess_pypyhome() { // glibc-only (dladdr is why we #define _GNU_SOURCE) // http://stackoverflow.com/questions/1681060/library-path-when-dynamically-loaded Dl_info info; void *_rpython_startup_code; _rpython_startup_code = dlsym(NULL,"rpython_startup_code"); if (_rpython_startup_code == NULL) { return NULL; } if (dladdr(_rpython_startup_code, &info) != 0) { const char* lib_path = info.dli_fname; char* lib_realpath = realpath(lib_path, NULL); if (lib_realpath == NULL) { return NULL; } char* pypy_home = change_basename(lib_realpath,"pypy"); free(lib_realpath); return pypy_home; } return NULL; } int main(int argc, char *argv[]) { // PyPy initialization char* pypy_home = getenv("PYPY_HOME"); int free_pypy_home = 0; if (pypy_home == NULL) { pypy_home = guess_pypyhome(); free_pypy_home = 1; } if (!pypy_home) { printf("PYPY_HOME was not specified and PYPY_HOME autodetection failed\n"); exit(1); } // actual pypy initialization rpython_startup_code(); pypy_setup_home(pypy_home,1); pypy_execute_source("import cffi; print('Hello World')\n"); pypy_init_threads(); // free stuff if (free_pypy_home == 1 && pypy_home != NULL) { free(pypy_home); } return 0; }
Také je potřeba linkovat oproti dl.
g++ -Wno-write-strings priklad-pypy_set_home.c -Lcesta_k_pypy -lpypy-c -ldl -Icesta_k_pypy/include -o priklad-pypy_set_home
Interface mezi PyPy a C kódem je nejlepší řešit pomocí CFFI (dokumentace). Funkci pypy_execute_source předáme python kód, který může nadefinovat callbacky apod.
A nakonec kompletní ukázka jak nadefinovat a volat callback v PyPy (python kód) z C.
#if !(_GNU_SOURCE) #define _GNU_SOURCE #endif #include <stdio.h> #include <libgen.h> #include <dlfcn.h> #include <unistd.h> #include <string.h> #include <stdlib.h> #include <PyPy.h> // caller should free returned pointer to avoid memleaks char* change_basename(char* full_path,char* new_basename) { char* full_path_copy = strdup(full_path); char* dirpath = dirname(full_path_copy); size_t new_path_len = strlen(dirpath)+1+strlen(new_basename); // +1 because of "/" char* new_path = (char*)malloc( new_path_len+1 ); // +1 because of terminating NULL memset(new_path, 0, new_path_len+1); strcat(new_path, dirpath); strcat(new_path, "/"); strcat(new_path, new_basename); free(full_path_copy); return new_path; } // caller should free returned pointer to avoid memleaks // returns NULL on error char* guess_pypyhome() { // glibc-only (dladdr is why we #define _GNU_SOURCE) // http://stackoverflow.com/questions/1681060/library-path-when-dynamically-loaded Dl_info info; void *_rpython_startup_code; _rpython_startup_code = dlsym(NULL,"rpython_startup_code"); if (_rpython_startup_code == NULL) { return NULL; } if (dladdr(_rpython_startup_code, &info) != 0) { const char* lib_path = info.dli_fname; char* lib_realpath = realpath(lib_path, NULL); if (lib_realpath == NULL) { return NULL; } char* pypy_home = change_basename(lib_realpath,"pypy"); free(lib_realpath); return pypy_home; } return NULL; } // caller should free returned pointer to avoid memleaks // returns NULL on error char* load_python_source(char* filepath) { char *buffer; long input_file_size; FILE *input_file = fopen(filepath, "rb"); if (input_file == NULL) { return NULL; } fseek(input_file, 0, SEEK_END); input_file_size = ftell(input_file); rewind(input_file); buffer = (char*)malloc(input_file_size * (sizeof(char))+1); memset(buffer, 0, input_file_size* (sizeof(char))+1); fread(buffer, sizeof(char), input_file_size, input_file); fclose(input_file); return buffer; } // caller should free returned pointer to avoid memleaks // returns NULL on error char* get_path_to_pyfile() { // Linux only // http://stackoverflow.com/questions/1023306/finding-current-executables-path-without-proc-self-exe // http://stackoverflow.com/questions/5919996/how-to-detect-reliably-mac-os-x-ios-linux-windows-in-c-preprocessor char* buf = realpath("/proc/self/exe",NULL); if (buf == NULL) { return NULL; } char* py_file = change_basename(buf,"setup.py"); free(buf); return py_file; } void (*callback)(long); int main(int argc, char *argv[]) { // PyPy initialization // prepare stuff char* pypy_home = getenv("PYPY_HOME"); int free_pypy_home = 0; if (pypy_home == NULL) { pypy_home = guess_pypyhome(); free_pypy_home = 1; } if (!pypy_home) { printf("PYPY_HOME was not specified and PYPY_HOME autodetection failed\n"); exit(1); } char* py_file = get_path_to_pyfile(); if (!py_file) { printf("Error determining path to the python setup file (setup.py)\n"); exit(1); } char* buffer = load_python_source(py_file); if (!buffer) { printf("Error opening python setup file (setup.py)\n"); exit(1); } // actual pypy initialization rpython_startup_code(); pypy_setup_home(pypy_home,1); pypy_execute_source( buffer ); pypy_init_threads(); // pypy_thread_attach(); // if multithreaded, call this in every thread which is calling into pypy // end of PyPy initialization for (int i=0; i<10; i++) { callback(42); } // free stuff if (free_pypy_home == 1 && pypy_home != NULL) { free(pypy_home); } if (py_file != NULL) { free(py_file); } if (buffer != NULL) { free(buffer); } return 0; }
Aby fungovaly callbacky s CFFI je potřeba překompilovat s flagem -export-dynamic
g++ -Wno-write-strings priklad-pypy.c -Lcesta_k_pypy -lpypy-c -ldl -Icesta_k_pypy/include -o priklad-pypy -export-dynamic
V tomto příkladu mám kód přímo v callback funkci v setup.py, v reálném nasazení by setup.py fungoval pouze jako vrstva mezi C a pythonem a volal uživatelský kód v jiném modulu, který by už o žádném C nebo CFFI nevěděl.
import os if "PYTHONPATH" in os.environ: import sys paths = os.environ["PYTHONPATH"].split(":") paths.reverse() for p in paths: sys.path.insert(0,p) import cffi ffi = cffi.FFI() ffi.cdef('void (*callback)(long);') lib = ffi.verify('void (*callback)(long);') def callback(i): print("yaay: %d" % i) cb = ffi.callback('void(long)',callback) lib.callback = cb
LD_LIBRARY_PATH=cesta_k_pypy ./priklad-pypy
Na první pohled se může zdát, že embedování PyPy do C je celkem složité, není tomu tak (vždyť kompletní příklad má kolem 150 řádků C kódu a většina z toho je copy&paste boilerplate). V mých (jednoduchých) testech jsem na problémy nenarazil, ale je potřeba mít na paměti, že embedování PyPy je těžce hipster feature.
Tiskni
Sdílej:
char* change_basename(char* full_path,char* new_basename) { char* full_path_copy = strdup(full_path); char* dirpath = dirname(full_path_copy); size_t new_path_len = strlen(dirpath)+1+strlen(new_basename); // +1 because of "/" char* new_path = (char*)malloc( new_path_len+1 ); // +1 because of terminating NULL memset(new_path, 0, new_path_len+1); strcat(new_path, dirpath); strcat(new_path, "/"); strcat(new_path, new_basename); free(full_path_copy); return new_path; }Proč full_patch a new_basename není
char const*
? Žádný z nich nemodifikujete a pro full_patch stejně děláte kopii. Ten memset je totálně zbytečnej, použití strcat, když už znáte délky řetězců z předchozích volání, je pitomost. No ono i ta zbytečná kopie full_path
je pitomost daná použití stupidní fce, která se nehodí.
Proč full_patch a new_basename není char const*?Protoze jsem maslo, tohle si fixnu.
Ten memset je totálně zbytečnejAno je, ale nicemu nevadi protoze se zavola behem behu programu asi dvakrat. Btw. kdybych ho jen vyhodil, tak ten c string nekonci \0. Ale chapu jak to myslis.
použití strcat, když už znáte délky řetězců z předchozích volání, je pitomost.hmmm?
No ono i ta zbytečná kopie full_path je pitomost daná použití stupidní fceCo jsem mel pouzit misto dirname? :-/
Btw. kdybych ho jen vyhodil, tak ten c string nekonci \0Stačí ti nastavit první byte na 0 pro první strcat(), ten ti nakonec dá další nulu pro další strcat(), ... ale jestli to voláš 2x, ok.
hmmm?
char* full_path_copy = strdup(full_path); char* dirpath = dirname(full_path_copy); size_t dirpath_len = strlen(dirpath); size_t new_basename_len = strlen(new_basename); size_t new_path_len = dirpath_len + new_basename_len + 1; char* new_path = (char*)malloc( new_path_len+1 ); memcpy(new_path, dirpath, dirpath_len); new_path[dirpath_len] = '/'; memcpy(new_path+dirpath_len+1, new_basename, new_basename_len); new_path[new_path_len] = 0; free(full_path_copy); return new_path;Nebo třeba použít stpcpy (POSIX only), ale memcpy je lepší, když už délku znám.
Co jsem mel pouzit misto dirname?Nějakej strchr() by mohl stačit. Problém s dirname je ten, že modifikuje řetězec a musíš tedy alokovat kopii, následně alokuješ další pro výsledný řetězec. To když je pak třeba v cyklu, tak to může být ee.