From 74363a481ed8edfef0d54349edbfbc748cf62101 Mon Sep 17 00:00:00 2001 From: Cesar Douady Date: Tue, 30 Apr 2024 11:41:17 +0200 Subject: [PATCH] improve ldebug -c + fix lack of dep on static dep --- Manifest | 1 + _lib/lmake_dbg.py | 6 +++--- src/autodep/clmake.cc | 1 + src/lmakeserver/cmd.cc | 15 +++++++------- src/lmakeserver/job.cc | 12 +++++------ src/lmakeserver/node.x.hh | 32 +++++++++++++++-------------- unit_tests/misc13.py | 42 +++++++++++++++++++++++++++++++++++++++ 7 files changed, 78 insertions(+), 31 deletions(-) create mode 100644 unit_tests/misc13.py diff --git a/Manifest b/Manifest index 2d0821ea..1ec92719 100644 --- a/Manifest +++ b/Manifest @@ -192,6 +192,7 @@ unit_tests/manual_target.py unit_tests/misc1.py unit_tests/misc10.py unit_tests/misc11.py +unit_tests/misc13.py unit_tests/misc2.py unit_tests/misc3.py unit_tests/misc4.py diff --git a/_lib/lmake_dbg.py b/_lib/lmake_dbg.py index c67a8c87..a8dea3d1 100644 --- a/_lib/lmake_dbg.py +++ b/_lib/lmake_dbg.py @@ -85,15 +85,15 @@ def run_pdb(dbg_dir,redirected,func,*args,**kwds) : def run_vscode(dbg_dir,redirected,func,*args,**kwds) : import json import os + import signal + signal.signal(signal.SIGPIPE,signal.SIG_IGN) # vscode seems to be sensitive to SIGPIPE try : # Write python process information to vscode debug workspace to allow gdb to attache to it workspace = dbg_dir + '/vscode/ldebug.code-workspace' if os.path.exists(workspace) : data = json.load(open(workspace)) for elmt in data['launch']['configurations'] : - if elmt.get('type') == 'by-gdb' : - if 'processId' in elmt : elmt['processId'] = os.getpid() - if 'program' in elmt : elmt['program' ] = open('/proc/self/cmdline').read().split('\x00')[:-1] + if elmt.get('type')=='by-gdb' and 'processId' in elmt : elmt['processId'] = os.getpid() with open(workspace,'w') as out : json.dump(data,out,indent='\t') out.write('\n') diff --git a/src/autodep/clmake.cc b/src/autodep/clmake.cc index 016f44f2..0ed06ac8 100644 --- a/src/autodep/clmake.cc +++ b/src/autodep/clmake.cc @@ -248,6 +248,7 @@ static PyObject* get_autodep( PyObject* /*null*/ , PyObject* args , PyObject* kw char c = 0/*garbage*/ ; // we have a private Record with a private AutodepEnv, so we must go through the backdoor to alter the regular AutodepEnv int rc [[maybe_unused]] = ::readlinkat( Record::s_root_fd() , (PrivateAdminDir+"/backdoor/autodep"s ).c_str() , &c , 1 ) ; + if (rc<0) return None.to_py_boost() ; SWEAR( c=='0' || c=='1' , int(c) ) ; SWEAR( rc==1 , rc ) ; return Ptr(c!='0')->to_py_boost() ; diff --git a/src/lmakeserver/cmd.cc b/src/lmakeserver/cmd.cc index e8b6c3a8..ec4164ee 100644 --- a/src/lmakeserver/cmd.cc +++ b/src/lmakeserver/cmd.cc @@ -265,7 +265,7 @@ R"({ "type" : "by-gdb" , "request" : "attach" , "name" : "Attach C/C++" - , "program" : "" + , "program" : $interpreter , "cwd" : $g_root_dir , "processId" : 0 } @@ -285,10 +285,11 @@ R"({ /**/ append_to_string( extensions , '"',ext,'"' ) ; first = false ; } - res = ::regex_replace( res , ::regex("\\$extensions") , extensions ) ; - res = ::regex_replace( res , ::regex("\\$name" ) , mk_json_str( j->name() ) ) ; - res = ::regex_replace( res , ::regex("\\$g_root_dir") , mk_json_str( *g_root_dir ) ) ; - res = ::regex_replace( res , ::regex("\\$program" ) , mk_json_str(to_string(*g_root_dir,'/',dbg_dir,"/cmd")) ) ; + res = ::regex_replace( res , ::regex("\\$extensions" ) , extensions ) ; + res = ::regex_replace( res , ::regex("\\$name" ) , mk_json_str( j->name() ) ) ; + res = ::regex_replace( res , ::regex("\\$g_root_dir" ) , mk_json_str( *g_root_dir ) ) ; + res = ::regex_replace( res , ::regex("\\$program" ) , mk_json_str(to_string(*g_root_dir,'/',dbg_dir,"/cmd")) ) ; + res = ::regex_replace( res , ::regex("\\$interpreter") , mk_json_str(to_string(start.interpreter[0] )) ) ; // ::vmap_ss env = _mk_env(start.env,report_end.end.dynamic_env) ; size_t kw = 13/*SEQUENCE_ID*/ ; for( auto&& [k,v] : env ) if (k!="TMPDIR") kw = ::max(kw,mk_json_str(k).size()) ; @@ -370,8 +371,8 @@ R"({ append_to_string( script , "args=()\n" ) ; append_to_string( script , "type code | grep -q .vscode-server || args+=( \"--user-data-dir ${DEBUG_DIR}/vscode/user\" )\n" ) ; for( Dep const& dep : j->deps ) - if (dep.dflags[Dflag::Static]) append_to_string( script , "args+=( ",mk_shell_str("-g "+dep->name()),")\n" ) ; // list dependences file to open in vscode - append_to_string( script , "args+=(\"-g ${DEBUG_DIR}/cmd\")\n" ) ; + if (dep.dflags[Dflag::Static]) append_to_string( script , "args+=( ",mk_shell_str(dep->name()),")\n" ) ; // list dependences file to open in vscode + append_to_string( script , "args+=(\"${DEBUG_DIR}/cmd\")\n" ) ; append_to_string( script , "args+=(\"${DEBUG_DIR}/vscode/ldebug.code-workspace\")\n" ) ; append_to_string( script , "code -n -w ${args[@]} &" ) ; } else { diff --git a/src/lmakeserver/job.cc b/src/lmakeserver/job.cc index da4cf235..813a46b2 100644 --- a/src/lmakeserver/job.cc +++ b/src/lmakeserver/job.cc @@ -838,17 +838,17 @@ namespace Engine { state.reason |= {JobReasonTag::DepMissingStatic,+dep} ; dep_err = RunStatus::MissingStatic ; } - continue ; // dont check modifs and errors + continue ; // dont check modifs and errors } if (!care) goto Continue ; - dep_modif = !dep.up_to_date() ; - if ( dep_modif && status==Status::Ok && dep.no_trigger() ) { // no_trigger only applies to successful jobs + dep_modif = !dep.up_to_date( is_static && modif ) ; // after a modified dep, consider static deps as being fully accessed to avoid reruns due to previous errors + if ( dep_modif && status==Status::Ok && dep.no_trigger() ) { // no_trigger only applies to successful jobs trace("no_trigger",dep) ; - req->no_triggers.emplace(dep,req->no_triggers.size()) ; // record to repeat in summary, value is just to order summary in discovery order + req->no_triggers.emplace(dep,req->no_triggers.size()) ; // record to repeat in summary, value is just to order summary in discovery order dep_modif = false ; } - if ( +state.stamped_err ) goto Continue ; // we are already in error, no need to analyze errors any further - if ( !is_static && modif ) goto Continue ; // if not static, errors may be washed by previous modifs, dont record them + if ( +state.stamped_err ) goto Continue ; // we are already in error, no need to analyze errors any further + if ( !is_static && modif ) goto Continue ; // if not static, errors may be washed by previous modifs, dont record them if ( dep_modif ) state.reason |= {JobReasonTag::DepOutOfDate,+dep} ; // switch (dnd.ok(*cdri,dep.accesses)) { diff --git a/src/lmakeserver/node.x.hh b/src/lmakeserver/node.x.hh index 7cfa44da..bacf7e64 100644 --- a/src/lmakeserver/node.x.hh +++ b/src/lmakeserver/node.x.hh @@ -157,7 +157,7 @@ namespace Engine { ::string accesses_str() const ; ::string dflags_str () const ; // services - bool up_to_date () const ; + bool up_to_date (bool full=false) const ; void acquire_crc() ; } ; static_assert(sizeof(Dep)==16) ; @@ -358,7 +358,7 @@ namespace Engine { void full_refresh( bool report_no_file , ::vector const& reqs , ::string const& name ) { set_buildable() ; if (is_src_anti()) refresh_src_anti(report_no_file,reqs,name) ; - else manual_refresh (Req() ) ; // no manual_steady diagnostic as this may be because of another job + else manual_refresh (Req() ) ; // no manual_steady diagnostic as this may be because of another job } // RuleIdx conform_idx( ) const { if (_conform_idx<=MaxRuleIdx) return _conform_idx ; else return NoIdx ; } @@ -374,7 +374,7 @@ namespace Engine { Job cj = conform_job() ; return +cj && ( cj->is_special() || has_actual_job(cj) ) ; } - Bool3 ok(bool force_err=false) const { // if Maybe <=> not built + Bool3 ok(bool force_err=false) const { // if Maybe <=> not built switch (status()) { case NodeStatus::Plain : return No | !( force_err || conform_job()->err() ) ; case NodeStatus::Multi : return No ; @@ -411,14 +411,16 @@ namespace Engine { } // // services - bool read(Accesses a) const { // return true <= file was perceived different from non-existent, assuming access provided in a - if (crc==Crc::None ) return false ; // file does not exist, cannot perceive difference - if (a[Access::Stat]) return true ; // if file exists, stat is different + bool read(Accesses a) const { // return true <= file was perceived different from non-existent, assuming access provided in a + if (crc==Crc::None ) return false ; // file does not exist, cannot perceive difference + if (a[Access::Stat]) return true ; // if file exists, stat is different if (crc.is_lnk() ) return a[Access::Lnk] ; if (crc.is_reg() ) return a[Access::Reg] ; - else return +a ; // dont know if file is a link, any access may have perceived a difference + else return +a ; // dont know if file is a link, any access may have perceived a difference + } + bool up_to_date( DepDigest const& dd , bool full=false ) const { // only manage crc, not dates + return crc.match( dd.crc() , full?~Accesses():dd.accesses ) ; } - bool up_to_date(DepDigest const& dd) const { return crc.match(dd.crc(),dd.accesses) ; } // only manage crc, not dates // Manual manual_wash( ReqInfo& ri , bool lazy=false ) ; // @@ -430,13 +432,13 @@ namespace Engine { ::c_vector_view prio_job_tgts (RuleIdx prio_idx) const ; ::c_vector_view conform_job_tgts (ReqInfo const& ) const ; ::c_vector_view conform_job_tgts ( ) const ; - ::c_vector_view candidate_job_tgts( ) const ; // all jobs above prio provided in conform_idx + ::c_vector_view candidate_job_tgts( ) const ; // all jobs above prio provided in conform_idx // - void set_buildable( Req={} , DepDepth lvl=0 ) ; // data independent, may be pessimistic (Maybe instead of Yes), req is for error reporing only + void set_buildable( Req={} , DepDepth lvl=0 ) ; // data independent, may be pessimistic (Maybe instead of Yes), req is for error reporing only void set_pressure ( ReqInfo& , CoarseDelay pressure ) const ; // void propag_speculate( Req req , Bool3 speculate ) const { - /**/ if (speculate==Yes ) return ; // fast path : nothing to propagate + /**/ if (speculate==Yes ) return ; // fast path : nothing to propagate ReqInfo& ri = req_info(req) ; if (speculate>=ri.speculate) return ; ri.speculate = speculate ; _propag_speculate(ri) ; @@ -454,7 +456,7 @@ namespace Engine { bool/*modified*/ refresh( Crc , SigDate const& ={} ) ; void refresh( ) ; private : - void _set_buildable_raw( Req , DepDepth ) ; // req is for error reporting only + void _set_buildable_raw( Req , DepDepth ) ; // req is for error reporting only bool/*done*/ _make_pre ( ReqInfo& ) ; void _make_raw ( ReqInfo& , MakeAction , Bool3 speculate=Yes ) ; void _set_pressure_raw ( ReqInfo& ) const ; @@ -463,7 +465,7 @@ namespace Engine { Buildable _gather_special_rule_tgts( ::string const& name ) ; Buildable _gather_prio_job_tgts ( ::string const& name , Req , DepDepth lvl=0 ) ; Buildable _gather_prio_job_tgts ( Req r , DepDepth lvl=0 ) { - if (!rule_tgts()) return Buildable::No ; // fast path : avoid computing name() + if (!rule_tgts()) return Buildable::No ; // fast path : avoid computing name() else return _gather_prio_job_tgts( name() , r , lvl ) ; } // @@ -612,8 +614,8 @@ namespace Engine { // Dep // - inline bool Dep::up_to_date() const { - return is_crc && crc().match((*this)->crc,accesses) ; + inline bool Dep::up_to_date(bool full) const { + return is_crc && crc().match( (*this)->crc , full?~Accesses():accesses ) ; } inline void Dep::acquire_crc() { diff --git a/unit_tests/misc13.py b/unit_tests/misc13.py new file mode 100644 index 00000000..4d7156ab --- /dev/null +++ b/unit_tests/misc13.py @@ -0,0 +1,42 @@ +# This file is part of the open-lmake distribution (git@github.com:cesar-douady/open-lmake.git) +# Copyright (c) 2023 Doliam +# This program is free software: you can redistribute/modify under the terms of the GPL-v3 (https://www.gnu.org/licenses/gpl-3.0.html). +# This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +if __name__!='__main__' : + + import sys + import time + + import lmake + from lmake.rules import Rule + + lmake.manifest = ( + 'Lmakefile.py' + , 'step.py' + ) + + from step import step + + class Hdep(Rule) : + target = r'hdep' + cmd = 'echo 2' + + class Sdep(Rule) : + target = r'sdep' + cmd = 'echo {step}' + + class Dut(Rule) : + target = 'dut' + dep = 'sdep' + cmd = '[ $(cat hdep) = {step} ] && cat sdep' + +else : + + import ut + + print('step=1',file=open('step.py','w')) + ut.lmake( 'dut' , may_rerun=1 , done=2 , failed=1 , rc=1 ) + + print('step=2',file=open('step.py','w')) + ut.lmake( 'dut' , done=2 ) # check sdep is not forgetten due to execution w/o hdep