project('clanker-saddle', ['c', 'vala'], version: '1.1', ) glib_dep = dependency('glib-2.0') gobject_dep = dependency('gobject-2.0') gio_dep = dependency('gio-2.0') json_glib_dep = dependency('json-glib-1.0') soup_dep = dependency('libsoup-3.0') libxml_dep = dependency('libxml-2.0') sqlite_dep = dependency('sqlite3') invercargill_dep = dependency('invercargill-1') invercargill_json_dep = dependency('invercargill-json') invercargill_sql_dep = dependency('invercargill-sql') invercargill_sql_inversion_dep = dependency('invercargill-sql-inversion') inversion_dep = dependency('inversion-0.1') astralis_dep = dependency('astralis-0.1') statum_dep = dependency('statum-0.1') spry_dep = dependency('spry-0.2') # The dev stack (spry 0.2 and co) lives in a non-standard prefix on # development machines (~/.local): meson's automatic build rpath makes # the BUILD-tree binary run, but `meson install` strips it, leaving the # installed binary unable to load libspry outside the build tree (the # canon_deploy bundle's deployed-boot check). Carry the dependency's # pkg-config libdir as the INSTALL rpath — inside a usm container the # same expression resolves the (harmless, existing) system libdir. devstack_libdir = spry_dep.get_variable(pkgconfig: 'libdir', default_value: '') # Web Push crypto primitives (P-256 ECDH/ECDSA + AES-128-GCM for VAPID # and RFC 8291 payload encryption — plan 11 task E). Found via the C # compiler (no Vala vapi exists for libcrypto; the shim is C-only). libcrypto_dep = meson.get_compiler('c').find_library('crypto') # Statum build tools — statum-mkpstm compiles pages, statum-mkres embeds # resources. Resolved from PATH at setup time. statum_mkpstm = find_program('statum-mkpstm') statum_mkres = find_program('statum-mkres') # ============================================================================= # Statum pages (statum-mkpstm): HTML → StatumPage subclass. Every page # composes the shared layout (main.html), which the tool discovers next to # the pages. # ============================================================================= template_deps = files( 'src/pages/main.html', # Partials (plan 16 R3, arch 16): inlined into pages at compile # time via ; discovered next to the pages. 'src/pages/goal_branches.html', 'src/pages/goal_compare.html', 'src/pages/progress_feed.html', 'src/pages/block_answer.html', 'src/pages/block_reply.html', 'src/pages/breadcrumb.html', ) # spry:pages-begin home_page = custom_target('home-page', input: 'src/pages/home.html', output: 'HomePage.vala', command: [statum_mkpstm, '-o', '@OUTPUT@', '-n', 'HomePage', '--ns', 'ClankerSaddle', '@INPUT@'], depend_files: template_deps ) login_page = custom_target('login-page', input: 'src/pages/login.html', output: 'LoginPage.vala', command: [statum_mkpstm, '-o', '@OUTPUT@', '-n', 'LoginPage', '--ns', 'ClankerSaddle', '@INPUT@'], depend_files: template_deps ) projects_page = custom_target('projects-page', input: 'src/pages/projects.html', output: 'ProjectsPage.vala', command: [statum_mkpstm, '-o', '@OUTPUT@', '-n', 'ProjectsPage', '--ns', 'ClankerSaddle', '@INPUT@'], depend_files: template_deps ) project_detail_page = custom_target('project-detail-page', input: 'src/pages/project_detail.html', output: 'ProjectDetailPage.vala', command: [statum_mkpstm, '-o', '@OUTPUT@', '-n', 'ProjectDetailPage', '--ns', 'ClankerSaddle', '@INPUT@'], depend_files: template_deps ) tasks_page = custom_target('tasks-page', input: 'src/pages/tasks.html', output: 'TasksPage.vala', command: [statum_mkpstm, '-o', '@OUTPUT@', '-n', 'TasksPage', '--ns', 'ClankerSaddle', '@INPUT@'], depend_files: template_deps ) task_detail_page = custom_target('task-detail-page', input: 'src/pages/task_detail.html', output: 'TaskDetailPage.vala', command: [statum_mkpstm, '-o', '@OUTPUT@', '-n', 'TaskDetailPage', '--ns', 'ClankerSaddle', '@INPUT@'], depend_files: template_deps ) task_log_page = custom_target('task-log-page', input: 'src/pages/task_log.html', output: 'TaskLogPage.vala', command: [statum_mkpstm, '-o', '@OUTPUT@', '-n', 'TaskLogPage', '--ns', 'ClankerSaddle', '@INPUT@'], depend_files: template_deps ) goals_page = custom_target('goals-page', input: 'src/pages/goals.html', output: 'GoalsPage.vala', command: [statum_mkpstm, '-o', '@OUTPUT@', '-n', 'GoalsPage', '--ns', 'ClankerSaddle', '@INPUT@'], depend_files: template_deps ) goal_detail_page = custom_target('goal-detail-page', input: 'src/pages/goal_detail.html', output: 'GoalDetailPage.vala', command: [statum_mkpstm, '-o', '@OUTPUT@', '-n', 'GoalDetailPage', '--ns', 'ClankerSaddle', '@INPUT@'], depend_files: template_deps ) saddles_page = custom_target('saddles-page', input: 'src/pages/saddles.html', output: 'SaddlesPage.vala', command: [statum_mkpstm, '-o', '@OUTPUT@', '-n', 'SaddlesPage', '--ns', 'ClankerSaddle', '@INPUT@'], depend_files: template_deps ) stacks_page = custom_target('stacks-page', input: 'src/pages/stacks.html', output: 'StacksPage.vala', command: [statum_mkpstm, '-o', '@OUTPUT@', '-n', 'StacksPage', '--ns', 'ClankerSaddle', '@INPUT@'], depend_files: template_deps ) stack_detail_page = custom_target('stack-detail-page', input: 'src/pages/stack_detail.html', output: 'StackDetailPage.vala', command: [statum_mkpstm, '-o', '@OUTPUT@', '-n', 'StackDetailPage', '--ns', 'ClankerSaddle', '@INPUT@'], depend_files: template_deps ) rules_page = custom_target('rules-page', input: 'src/pages/rules.html', output: 'RulesPage.vala', command: [statum_mkpstm, '-o', '@OUTPUT@', '-n', 'RulesPage', '--ns', 'ClankerSaddle', '@INPUT@'], depend_files: template_deps ) discoveries_page = custom_target('discoveries-page', input: 'src/pages/discoveries.html', output: 'DiscoveriesPage.vala', command: [statum_mkpstm, '-o', '@OUTPUT@', '-n', 'DiscoveriesPage', '--ns', 'ClankerSaddle', '@INPUT@'], depend_files: template_deps ) chats_page = custom_target('chats-page', input: 'src/pages/chats.html', output: 'ChatsPage.vala', command: [statum_mkpstm, '-o', '@OUTPUT@', '-n', 'ChatsPage', '--ns', 'ClankerSaddle', '@INPUT@'], depend_files: template_deps ) chat_detail_page = custom_target('chat-detail-page', input: 'src/pages/chat_detail.html', output: 'ChatDetailPage.vala', command: [statum_mkpstm, '-o', '@OUTPUT@', '-n', 'ChatDetailPage', '--ns', 'ClankerSaddle', '@INPUT@'], depend_files: template_deps ) users_page = custom_target('users-page', input: 'src/pages/users.html', output: 'UsersPage.vala', command: [statum_mkpstm, '-o', '@OUTPUT@', '-n', 'UsersPage', '--ns', 'ClankerSaddle', '@INPUT@'], depend_files: template_deps ) model_settings_page = custom_target('model-settings-page', input: 'src/pages/model_settings.html', output: 'ModelSettingsPage.vala', command: [statum_mkpstm, '-o', '@OUTPUT@', '-n', 'ModelSettingsPage', '--ns', 'ClankerSaddle', '@INPUT@'], depend_files: template_deps ) profile_page = custom_target('profile-page', input: 'src/pages/profile.html', output: 'ProfilePage.vala', command: [statum_mkpstm, '-o', '@OUTPUT@', '-n', 'ProfilePage', '--ns', 'ClankerSaddle', '@INPUT@'], depend_files: template_deps ) # spry:pages-end # ============================================================================= # Embedded resources (statum-mkres): the stylesheet precompressed at build # time and served from /_statum/resource/app.css. # ============================================================================= app_css = custom_target('app-css', input: 'src/pages/app.css', output: 'AppCss.vala', command: [statum_mkres, '-o', '@OUTPUT@', '-n', 'app.css', '--class-name', 'AppCss', '--ns', 'ClankerSaddle', '-c', 'text/css', '@INPUT@'], ) app_js = custom_target('app-js', input: 'src/pages/app.js', output: 'AppJs.vala', command: [statum_mkres, '-o', '@OUTPUT@', '-n', 'app.js', '--class-name', 'AppJs', '--ns', 'ClankerSaddle', '-c', 'text/javascript', '@INPUT@'], ) task_log_js = custom_target('task-log-js-res', input: 'src/pages/task_log.js', output: 'TaskLogJs.vala', command: [statum_mkres, '-o', '@OUTPUT@', '-n', 'task_log.js', '--class-name', 'TaskLogJs', '--ns', 'ClankerSaddle', '-c', 'text/javascript', '@INPUT@'], ) favicon_png = custom_target('favicon-png', input: 'src/clanker-saddle.png', output: 'FaviconResource.vala', command: [statum_mkres, '-o', '@OUTPUT@', '-n', 'clanker-saddle.png', '--class-name', 'FaviconResource', '--ns', 'ClankerSaddle', '-c', 'image/png', '@INPUT@'], ) emblem_png = custom_target('emblem-png', input: 'src/clanker-saddle-emblem.png', output: 'EmblemResource.vala', command: [statum_mkres, '-o', '@OUTPUT@', '-n', 'clanker-saddle-emblem.png', '--class-name', 'EmblemResource', '--ns', 'ClankerSaddle', '-c', 'image/png', '@INPUT@'], ) # ============================================================================= # Sources # ============================================================================= app_sources = files( # spry:sources-begin 'src/main.vala', 'src/Json.vala', 'src/Format.vala', 'src/Data/Entities.vala', 'src/Data/Migrations/M0001_Initial.vala', 'src/Data/Migrations/M0002_BatchImprovements.vala', 'src/Data/Migrations/M0003_EventIndexes.vala', 'src/Data/Migrations/M0004_ProjectUnderstandings.vala', 'src/Data/Migrations/M0005_Discoveries.vala', 'src/Data/Migrations/M0006_Advisor.vala', 'src/Data/Migrations/M0007_ParkingGuard.vala', 'src/Data/Migrations/M0008_AnswerWake.vala', 'src/Data/Migrations/M0009_TurnCapPark.vala', 'src/Data/Migrations/M0010_ModelConfig.vala', 'src/Data/Migrations/M0011_UserPreferences.vala', 'src/Services/ProjectService.vala', 'src/Services/TaskService.vala', 'src/Services/GoalService.vala', 'src/Services/SaddleService.vala', 'src/Services/EventService.vala', 'src/Services/LifecycleService.vala', 'src/Services/SaddlePollState.vala', 'src/Services/AnswerDeliveryHub.vala', 'src/Services/AllocationService.vala', 'src/Services/StatePulse.vala', 'src/Services/Dispatcher.vala', 'src/Services/RuleService.vala', 'src/Services/StackService.vala', 'src/Services/NoteService.vala', 'src/Services/UnderstandingService.vala', 'src/Services/DiscoveryService.vala', 'src/Services/CapabilityService.vala', 'src/Services/TimelineService.vala', 'src/Services/PushService.vala', 'src/Services/ChatService.vala', 'src/Services/ModelConfigService.vala', 'src/Services/UserPreferenceService.vala', 'src/Services/CanonService.vala', 'src/Services/AdvisorService.vala', 'src/Services/AdvisorCallback.vala', 'src/Api/SaddleApi.vala', 'src/Api/ProjectApi.vala', 'src/Api/AdvisorApi.vala', 'src/Api/EventLogApi.vala', 'src/Api/PushApi.vala', 'src/actions/Forms.vala', 'src/actions/LoginAction.vala', 'src/actions/ProjectActions.vala', 'src/actions/TaskActions.vala', 'src/actions/GoalActions.vala', 'src/actions/DashboardActions.vala', 'src/actions/SaddleActions.vala', 'src/actions/RuleActions.vala', 'src/actions/DiscoveryActions.vala', 'src/actions/AdvisorActions.vala', 'src/actions/StackActions.vala', 'src/actions/NoteActions.vala', 'src/actions/CapabilityActions.vala', 'src/actions/UserActions.vala', 'src/actions/ModelActions.vala', 'src/actions/ProfileActions.vala', 'src/actions/PushActions.vala', 'src/entrypoints/HomeEntrypoint.vala', 'src/entrypoints/LoginEntrypoint.vala', 'src/entrypoints/ProjectsEntrypoint.vala', 'src/entrypoints/TasksEntrypoint.vala', 'src/entrypoints/TaskDetailEntrypoint.vala', 'src/entrypoints/TaskLogEntrypoint.vala', 'src/entrypoints/GoalDetailEntrypoint.vala', 'src/entrypoints/SaddlesEntrypoint.vala', 'src/entrypoints/RulesEntrypoint.vala', 'src/entrypoints/DiscoveriesEntrypoint.vala', 'src/entrypoints/ChatsEntrypoint.vala', 'src/entrypoints/ChatDetailEntrypoint.vala', 'src/entrypoints/StacksEntrypoint.vala', 'src/entrypoints/UsersEntrypoint.vala', 'src/entrypoints/ModelSettingsEntrypoint.vala', 'src/entrypoints/ProfileEntrypoint.vala', # spry:sources-end ) # Web Push crypto shim (plan 11 task E): C over OpenSSL's libcrypto — # P-256 keygen/ECDH/ECDSA + AES-128-GCM behind web-push-crypto.vapi. push_crypto = files('src/Push/web-push-crypto.c') app_generated = [ # spry:generated-begin home_page, login_page, projects_page, project_detail_page, tasks_page, task_detail_page, task_log_page, goals_page, goal_detail_page, saddles_page, rules_page, discoveries_page, chats_page, chat_detail_page, stacks_page, stack_detail_page, users_page, model_settings_page, profile_page, # spry:generated-end ] clanker_saddle_exe = executable('clanker-saddle', app_sources + app_generated + [app_css, app_js, task_log_js, favicon_png, emblem_png, push_crypto], include_directories: include_directories('src/Push'), vala_args: ['--vapidir=' + join_paths(meson.current_source_dir(), 'vapi')], dependencies: [ spry_dep, statum_dep, astralis_dep, inversion_dep, invercargill_sql_inversion_dep, invercargill_sql_dep, invercargill_json_dep, invercargill_dep, sqlite_dep, libxml_dep, soup_dep, json_glib_dep, gio_dep, gobject_dep, glib_dep, libcrypto_dep, ], install: true, install_rpath: devstack_libdir ) # The canonical texts ride the install into # datadir/clanker-saddle/canon; the service resolves that layout at # run time with a source-tree fallback (the Saddle daemon mirror — # the web is the canon's owner now). canon_files = files( 'canon/gnu-manifesto.md', 'canon/free-software-definition.md', 'canon/open-source-misses-the-point.md', 'canon/right-to-read.md', 'canon/selling-free-software.md', 'canon/selling-exceptions.md', 'canon/words-to-avoid.md', 'canon/misinterpreting-copyright.md', 'canon/university-and-the-web.md', 'canon/plato-apology.md', 'canon/plato-republic-vi.md', 'canon/aristotle-nicomachean-ethics-i-ii.md', 'canon/aquinas-treatise-on-happiness.md', 'canon/lewis-abolition-of-man-i-ii.md', 'canon/magnifica-humanitas-summary.md') install_data(canon_files, install_dir: get_option('datadir') / 'clanker-saddle' / 'canon') # ============================================================================= # Tests: the web-stack suite is a package (plan 16 R5, arch 18) — # test/web_stack_test.py is the entry shim meson invokes; the shared # machinery (fake saddle daemon, wire helpers, fixtures, the runner) # lives in test/harness.py and one module per scenario bundle # in test/bundles/. The default run is SEQUENTIAL (the unbounded # parallel fan-out OOM'd the machine; --parallel N opts back in with a # concurrency cap), so the timeout covers the summed bundle times. # ============================================================================= python_test = import('python').find_installation('python3') test('web-stack', python_test, args: [files('test/web_stack_test.py'), clanker_saddle_exe], timeout: 3600, ) # The event-log page's client JS: static source checks plus a python # port of the ingest/ordering/scroll-stick logic. Standalone by # design - it must run where the app binary cannot be built (the # statum toolchain is a build-time dependency that not every # container carries), so it reads src/pages/task_log.html directly # instead of driving a live server. test('task-log-js', python_test, args: [files('test/test_task_log_js.py')], timeout: 120, )