Changeset a97b9ed
- Timestamp:
- Oct 16, 2023, 8:09:51 AM (14 months ago)
- Branches:
- master
- Children:
- 2bf46a5, 54e59dd, 61e5d99
- Parents:
- 946a6e4 (diff), 8cbe732 (diff)
Note: this is a merge changeset, the changes displayed below correspond to the merge itself.
Use the(diff)
links above to see all the changes relative to each parent. - Files:
-
- 3 added
- 24 edited
Legend:
- Unmodified
- Added
- Removed
-
libcfa/src/concurrency/channel.hfa
r946a6e4 ra97b9ed 130 130 static inline void __cons_handoff( channel(T) & chan, T & elem ) with(chan) { 131 131 memcpy( cons`first.extra, (void *)&elem, sizeof(T) ); // do waiting consumer work 132 __atomic_thread_fence( __ATOMIC_SEQ_CST ); 132 133 wake_one( cons ); 133 134 } … … 136 137 static inline void __prods_handoff( channel(T) & chan, T & retval ) with(chan) { 137 138 memcpy( (void *)&retval, prods`first.extra, sizeof(T) ); 139 __atomic_thread_fence( __ATOMIC_SEQ_CST ); 138 140 wake_one( prods ); 139 141 } -
libcfa/src/concurrency/cofor.cfa
r946a6e4 ra97b9ed 4 4 // cofor ( uC++ COFOR ) 5 5 6 thread co _runner {6 thread cofor_runner { 7 7 ssize_t low, high; 8 8 __cofor_body_t loop_body; 9 9 }; 10 10 11 static void ?{}( co _runner & this, ssize_t low, ssize_t high, __cofor_body_t loop_body ) {11 static void ?{}( cofor_runner & this, ssize_t low, ssize_t high, __cofor_body_t loop_body ) { 12 12 this.low = low; 13 13 this.high = high; … … 15 15 } 16 16 17 void main( co _runner & this ) with( this ) {17 void main( cofor_runner & this ) with( this ) { 18 18 for ( ssize_t i = low; i < high; i++ ) 19 19 loop_body(i); … … 29 29 ssize_t i = 0; 30 30 ssize_t stride_iter = low; 31 co _runner * runners[ threads ];31 cofor_runner * runners[ threads ]; 32 32 for ( i; threads ) { 33 33 runners[i] = alloc(); … … 45 45 } 46 46 47 //////////////////////////////////////////////////////////////////////////////////////////48 // parallel (COBEGIN/COEND)49 47 50 thread para_runner {51 parallel_stmt_t body;52 void * arg;53 };54 55 static void ?{}( para_runner & this, parallel_stmt_t body, void * arg ) {56 this.body = body;57 this.arg = arg;58 }59 60 void main( para_runner & this ) with( this ) { body( arg ); }61 62 void parallel( parallel_stmt_t * stmts, void ** args, size_t num ) libcfa_public {63 para_runner * runners[ num ];64 for ( i; num )65 (*(runners[i] = malloc())){ stmts[i], args[i] };66 for ( i; num )67 delete( runners[i] );68 }69 -
libcfa/src/concurrency/cofor.hfa
r946a6e4 ra97b9ed 16 16 17 17 ////////////////////////////////////////////////////////////////////////////////////////// 18 // parallel (COBEGIN/COEND) 19 typedef void (*parallel_stmt_t)( void * ); 18 // corun 20 19 21 void parallel( parallel_stmt_t * stmts, void ** args, size_t num ); 20 // 21 typedef void (*__CFA_corun_lambda_t)( void ); 22 23 // used to run a corun statement in parallel 24 thread co_runner { 25 __CFA_corun_lambda_t body; 26 }; 27 28 // wraps a co_runner to provide RAII deallocation 29 struct runner_block { 30 co_runner * runner; 31 }; 32 static inline void ?{}( co_runner & this, __CFA_corun_lambda_t body ) { this.body = body; } 33 34 void main( co_runner & this ) with( this ) { body(); } 35 36 static inline void ?{}( runner_block & this ) {} 37 static inline void ?{}( runner_block & this, __CFA_corun_lambda_t body ) { 38 (*(this.runner = malloc())){ body }; 39 } 40 41 static inline void ^?{}( runner_block & this ) { 42 delete( this.runner ); 43 } 44 -
src/AST/Convert.cpp
r946a6e4 ra97b9ed 269 269 node->location, 270 270 Type::StorageClasses( node->storage.val ), 271 271 get<Type>().accept1( node->base ), 272 272 LinkageSpec::Spec( node->linkage.val ) 273 273 ); … … 567 567 } 568 568 569 569 const ast::WhenClause * visit( const ast::WhenClause * node ) override final { 570 570 // There is no old-AST WhenClause, so this should never be called. 571 571 assert( !node ); … … 604 604 } 605 605 606 607 606 const ast::Stmt * visit( const ast::WaitUntilStmt * node ) override final { 607 // There is no old-AST WaitUntilStmt, so this should never be called. 608 608 assert( !node ); 609 609 return nullptr; … … 648 648 ); 649 649 return stmtPostamble( stmt, node ); 650 } 651 652 const ast::Stmt * visit( const ast::CorunStmt * node ) override final { 653 // There is no old-AST CorunStmt, so this should never be called. 654 assert( !node ); 655 return nullptr; 650 656 } 651 657 … … 853 859 // New workd: one public type: node->result, plus node->underlyer only to support roundtrip conversion 854 860 // preserving underlyer because the correct type for string literals is complicated to construct, 855 861 // and distinguishing a string from other literals using the type is hard to do accurately 856 862 // Both worlds: the outer, expression-level type can change during resolution 857 863 // for a string, that's char[k] before-resolve and char * after … … 859 865 // for a string, that's char[k] always 860 866 // Both worlds: the "rep" field of a constant is the C source file fragment that compiles to the desired value 861 867 // for a string, that includes outer quotes, backslashes, et al cases from the Literals test 862 868 ConstantExpr *rslt = new ConstantExpr(Constant( 863 869 get<Type>().accept1(node->underlyer), … … 1518 1524 return strict_dynamic_cast< ast::Decl * >( node ); 1519 1525 } 1520 1526 1521 1527 ConverterOldToNew() = default; 1522 1528 ConverterOldToNew(const ConverterOldToNew &) = delete; … … 1581 1587 ast::Label make_label(const Label* old) { 1582 1588 CodeLocation const & location = 1583 1589 ( old->labelled ) ? old->labelled->location : CodeLocation(); 1584 1590 return ast::Label( 1585 1591 location, … … 2240 2246 // TypeSubstitution shouldn't exist yet in old. 2241 2247 ast::TypeSubstitution * convertTypeSubstitution(const TypeSubstitution * old) { 2242 2243 2248 if (!old) return nullptr; 2244 2249 if (old->empty()) return nullptr; … … 2285 2290 ast::Expr * visitBaseExpr_SkipResultType( const Expression * old, ast::Expr * nw) { 2286 2291 2287 nw->env 2292 nw->env = convertTypeSubstitution(old->env); 2288 2293 2289 2294 nw->extension = old->extension; … … 2856 2861 2857 2862 virtual void visit( const EnumInstType * old ) override final { 2858 ast::EnumInstType * ty; 2863 ast::EnumInstType * ty; 2859 2864 if ( old->baseEnum ) { 2860 2865 ty = new ast::EnumInstType{ -
src/AST/Fwd.hpp
r946a6e4 ra97b9ed 67 67 class ImplicitCtorDtorStmt; 68 68 class MutexStmt; 69 class CorunStmt; 69 70 70 71 class Expr; -
src/AST/Node.cpp
r946a6e4 ra97b9ed 192 192 template class ast::ptr_base< ast::MutexStmt, ast::Node::ref_type::weak >; 193 193 template class ast::ptr_base< ast::MutexStmt, ast::Node::ref_type::strong >; 194 template class ast::ptr_base< ast::CorunStmt, ast::Node::ref_type::weak >; 195 template class ast::ptr_base< ast::CorunStmt, ast::Node::ref_type::strong >; 194 196 template class ast::ptr_base< ast::Expr, ast::Node::ref_type::weak >; 195 197 template class ast::ptr_base< ast::Expr, ast::Node::ref_type::strong >; -
src/AST/Pass.hpp
r946a6e4 ra97b9ed 162 162 const ast::FinallyClause * visit( const ast::FinallyClause * ) override final; 163 163 const ast::Stmt * visit( const ast::SuspendStmt * ) override final; 164 164 const ast::WhenClause * visit( const ast::WhenClause * ) override final; 165 165 const ast::Stmt * visit( const ast::WaitForStmt * ) override final; 166 166 const ast::WaitForClause * visit( const ast::WaitForClause * ) override final; 167 167 const ast::Stmt * visit( const ast::WaitUntilStmt * ) override final; 168 168 const ast::Decl * visit( const ast::WithStmt * ) override final; 169 169 const ast::NullStmt * visit( const ast::NullStmt * ) override final; … … 171 171 const ast::Stmt * visit( const ast::ImplicitCtorDtorStmt * ) override final; 172 172 const ast::Stmt * visit( const ast::MutexStmt * ) override final; 173 const ast::Stmt * visit( const ast::CorunStmt * ) override final; 173 174 const ast::Expr * visit( const ast::ApplicationExpr * ) override final; 174 175 const ast::Expr * visit( const ast::UntypedExpr * ) override final; -
src/AST/Pass.impl.hpp
r946a6e4 ra97b9ed 1121 1121 1122 1122 //-------------------------------------------------------------------------- 1123 // CorunStmt 1124 template< typename core_t > 1125 const ast::Stmt * ast::Pass< core_t >::visit( const ast::CorunStmt * node ) { 1126 VISIT_START( node ); 1127 1128 if ( __visit_children() ) { 1129 maybe_accept( node, &CorunStmt::stmt ); 1130 } 1131 1132 VISIT_END( Stmt, node ); 1133 } 1134 1135 //-------------------------------------------------------------------------- 1123 1136 // ApplicationExpr 1124 1137 template< typename core_t > -
src/AST/Print.cpp
r946a6e4 ra97b9ed 209 209 } 210 210 211 211 void print( const ast::WaitStmt * node ) { 212 212 if ( node->timeout_time ) { 213 213 os << indent-1 << "timeout of:" << endl; … … 860 860 } 861 861 862 862 virtual const ast::Stmt * visit( const ast::WaitUntilStmt * node ) override final { 863 863 os << "Waituntil Statement" << endl; 864 864 indent += 2; … … 866 866 clause->accept( *this ); 867 867 } 868 print(node); // calls print( const ast::WaitStmt * node ) 868 // calls print( const ast::WaitStmt * node ) 869 print(node); 869 870 return node; 870 871 } … … 913 914 printAll( node->mutexObjs ); 914 915 --indent; 916 os << indent << "... with Statement: "; 917 ++indent; 918 safe_print( node->stmt ); 919 --indent; 920 os << endl; 921 922 return node; 923 } 924 925 virtual const ast::Stmt * visit( const ast::CorunStmt * node ) override final { 926 os << "Corun Statement" << endl; 915 927 os << indent << "... with Statement: "; 916 928 ++indent; -
src/AST/Stmt.hpp
r946a6e4 ra97b9ed 532 532 }; 533 533 534 // Corun Statement 535 class CorunStmt final : public Stmt { 536 public: 537 ptr<Stmt> stmt; 538 539 CorunStmt( const CodeLocation & loc, const Stmt * stmt, const std::vector<Label> && labels = {} ) 540 : Stmt(loc, std::move(labels)), stmt(stmt) {} 541 542 const Stmt * accept( Visitor & v ) const override { return v.visit( this ); } 543 private: 544 CorunStmt * clone() const override { return new CorunStmt{ *this }; } 545 MUTATE_FRIEND 546 }; 547 534 548 } // namespace ast 535 549 -
src/AST/Visitor.hpp
r946a6e4 ra97b9ed 59 59 virtual const ast::Stmt * visit( const ast::ImplicitCtorDtorStmt * ) = 0; 60 60 virtual const ast::Stmt * visit( const ast::MutexStmt * ) = 0; 61 virtual const ast::Stmt * visit( const ast::CorunStmt * ) = 0; 61 62 virtual const ast::Expr * visit( const ast::ApplicationExpr * ) = 0; 62 63 virtual const ast::Expr * visit( const ast::UntypedExpr * ) = 0; -
src/Common/CodeLocationTools.cpp
r946a6e4 ra97b9ed 137 137 macro(ImplicitCtorDtorStmt, Stmt) \ 138 138 macro(MutexStmt, Stmt) \ 139 macro(CorunStmt, Stmt) \ 139 140 macro(ApplicationExpr, Expr) \ 140 141 macro(UntypedExpr, Expr) \ -
src/Concurrency/module.mk
r946a6e4 ra97b9ed 18 18 Concurrency/Actors.cpp \ 19 19 Concurrency/Actors.hpp \ 20 Concurrency/Corun.cpp \ 21 Concurrency/Corun.hpp \ 20 22 Concurrency/KeywordsNew.cpp \ 21 23 Concurrency/Keywords.cc \ -
src/GenPoly/BoxNew.cpp
r946a6e4 ra97b9ed 94 94 95 95 /// Adds parameters for otype size and alignment to a function type. 96 void add OTypeParams(97 ast:: FunctionDecl * decl,96 void addSTypeParams( 97 ast::vector<ast::DeclWithType> & params, 98 98 ast::vector<ast::TypeDecl> const & sizedParams ) { 99 // TODO: Can we fold this into buildLayoutFunction to avoid rebuilding?100 ast::FunctionType * type = ast::mutate( decl->type.get() );101 99 for ( ast::ptr<ast::TypeDecl> const & sizedParam : sizedParams ) { 102 100 ast::TypeInstType inst( sizedParam ); 103 101 std::string paramName = Mangle::mangleType( &inst ); 104 decl->params.emplace_back( new ast::ObjectDecl(102 params.emplace_back( new ast::ObjectDecl( 105 103 sizedParam->location, 106 104 sizeofName( paramName ), 107 105 makeSizeAlignType() 108 106 ) ); 109 type->params.emplace_back( makeSizeAlignType() ); 110 decl->params.emplace_back( new ast::ObjectDecl( 107 params.emplace_back( new ast::ObjectDecl( 111 108 sizedParam->location, 112 109 alignofName( paramName ), 113 110 makeSizeAlignType() 114 111 ) ); 115 type->params.emplace_back( makeSizeAlignType() ); 116 } 117 decl->type = type; 112 } 118 113 } 119 114 … … 129 124 }; 130 125 131 // TODO: Is there a better way to handle the different besides a flag?132 126 LayoutData buildLayoutFunction( 133 127 CodeLocation const & location, ast::AggregateDecl const * aggr, 128 ast::vector<ast::TypeDecl> const & sizedParams, 134 129 bool isInFunction, bool isStruct ) { 135 130 ast::ObjectDecl * sizeParam = new ast::ObjectDecl( … … 153 148 params.push_back( offsetParam ); 154 149 } 150 addSTypeParams( params, sizedParams ); 155 151 156 152 // Routines at global scope marked "static" to prevent multiple … … 238 234 239 235 // Build layout function signature. 240 LayoutData layout = 241 buildLayoutFunction( location, decl, isInFunction(), true );236 LayoutData layout = buildLayoutFunction( 237 location, decl, sizedParams, isInFunction(), true ); 242 238 ast::FunctionDecl * layoutDecl = layout.function; 243 239 // Also return these or extract them from the parameter list? … … 246 242 ast::ObjectDecl const * offsetofParam = layout.offsetofParam; 247 243 assert( nullptr != layout.offsetofParam ); 248 addOTypeParams( layoutDecl, sizedParams );249 244 250 245 // Calculate structure layout in function body. … … 313 308 314 309 // Build layout function signature. 315 LayoutData layout = 316 buildLayoutFunction( location, decl, isInFunction(), false );310 LayoutData layout = buildLayoutFunction( 311 location, decl, sizedParams, isInFunction(), false ); 317 312 ast::FunctionDecl * layoutDecl = layout.function; 318 313 // Also return these or extract them from the parameter list? … … 320 315 ast::ObjectDecl const * alignofParam = layout.alignofParam; 321 316 assert( nullptr == layout.offsetofParam ); 322 addOTypeParams( layoutDecl, sizedParams );323 317 324 318 // Calculate union layout in function body. … … 641 635 // the variable can be reused as a parameter to the call rather than 642 636 // creating a new temporary variable. Previously this step was an 643 // optimization, but 644 // ... 645 // with the introduction of tuples and UniqueExprs, it is necessary to 646 // ensure that they use the same variable. 637 // optimization, but with the introduction of tuples and UniqueExprs, 638 // it is necessary to ensure that they use the same variable. 647 639 // Essentially, looking for pattern: 648 640 // (x=f(...), x) … … 689 681 // the distinction between _conc_T30 and T3(int)) concRetType may not be 690 682 // a good name in one or both of these places. 691 // TODO A more appropriate name change is welcome.692 683 if ( dynRetType ) { 693 684 ast::Type const * result = mutExpr->result; … … 698 689 } else if ( needsAdapter( function, scopeTypeVars ) 699 690 && !needsAdapter( function, exprTypeVars ) ) { 700 // TODO:701 // The !needsAdapter check may be incorrect. It seems there is some702 // situation where an adapter is applied where it shouldn't be,703 // and this fixes it for some case. More investigation is needed.704 705 691 // Change the application so it calls the adapter rather than the 706 692 // passed function. … … 1774 1760 /// Adds type parameters to the layout call; will generate the 1775 1761 /// appropriate parameters if needed. 1776 void add OTypeParamsToLayoutCall(1762 void addSTypeParamsToLayoutCall( 1777 1763 ast::UntypedExpr * layoutCall, 1778 1764 const ast::vector<ast::Type> & otypeParams ); … … 2014 2000 ast::MemberExpr const * expr ) { 2015 2001 // Only mutate member expressions for polymorphic types. 2016 int typeDepth;2017 2002 ast::Type const * objectType = hasPolyBase( 2018 expr->aggregate->result, scopeTypeVars , &typeDepth2003 expr->aggregate->result, scopeTypeVars 2019 2004 ); 2020 2005 if ( !objectType ) return expr; … … 2094 2079 } 2095 2080 // MemberExpr was converted to pointer + offset; and it is not valid C to 2096 // take the address of an addition, so strip tthe address-of.2081 // take the address of an addition, so strip away the address-of. 2097 2082 // It also preserves the env value. 2098 2083 return ast::mutate_field( expr->arg.get(), &ast::Expr::env, expr->env ); … … 2295 2280 } ); 2296 2281 2297 add OTypeParamsToLayoutCall( layoutCall, sizedParams );2282 addSTypeParamsToLayoutCall( layoutCall, sizedParams ); 2298 2283 2299 2284 stmtsToAddBefore.emplace_back( … … 2336 2321 } ); 2337 2322 2338 add OTypeParamsToLayoutCall( layoutCall, sizedParams );2323 addSTypeParamsToLayoutCall( layoutCall, sizedParams ); 2339 2324 2340 2325 stmtsToAddBefore.emplace_back( … … 2346 2331 } 2347 2332 2348 void PolyGenericCalculator::add OTypeParamsToLayoutCall(2333 void PolyGenericCalculator::addSTypeParamsToLayoutCall( 2349 2334 ast::UntypedExpr * layoutCall, 2350 2335 const ast::vector<ast::Type> & otypeParams ) { -
src/Parser/StatementNode.cc
r946a6e4 ra97b9ed 498 498 } // build_mutex 499 499 500 ast::Stmt * build_corun( const CodeLocation & location, StatementNode * stmt ) { 501 ast::Stmt * body = maybeMoveBuild( stmt ); 502 return new ast::CorunStmt( location, body ); 503 } // build_corun 504 500 505 // Local Variables: // 501 506 // tab-width: 4 // -
src/Parser/StatementNode.h
r946a6e4 ra97b9ed 105 105 ast::Stmt * build_with( const CodeLocation &, ExpressionNode * exprs, StatementNode * stmt ); 106 106 ast::Stmt * build_mutex( const CodeLocation &, ExpressionNode * exprs, StatementNode * stmt ); 107 ast::Stmt * build_corun( const CodeLocation &, StatementNode * stmt ); -
src/Parser/parser.yy
r946a6e4 ra97b9ed 1721 1721 corun_statement: 1722 1722 CORUN statement 1723 { SemanticError( yylloc, "corun statement is currently unimplemented." ); $$ = nullptr; }1723 { $$ = new StatementNode( build_corun( yylloc, $2 ) ); } 1724 1724 ; 1725 1725 -
src/ResolvExpr/ResolveTypeof.cc
r946a6e4 ra97b9ed 231 231 const ast::Designation *des = mutListInit->designations[k].get(); 232 232 // Desination here 233 ast::Designation * newDesi nation = new ast::Designation(des->location);233 ast::Designation * newDesignation = new ast::Designation(des->location); 234 234 std::deque<ast::ptr<ast::Expr>> newDesignators; 235 235 … … 265 265 } 266 266 267 newDesi nation->designators = newDesignators;268 mutListInit = ast::mutate_field_index(mutListInit, &ast::ListInit::designations, k, newDesi nation);267 newDesignation->designators = newDesignators; 268 mutListInit = ast::mutate_field_index(mutListInit, &ast::ListInit::designations, k, newDesignation); 269 269 270 270 } -
src/main.cc
r946a6e4 ra97b9ed 46 46 #include "Common/utility.h" // for deleteAll, filter, printAll 47 47 #include "Concurrency/Actors.hpp" // for implementActors 48 #include "Concurrency/Corun.hpp" // for implementCorun 48 49 #include "Concurrency/Keywords.h" // for implementMutex, implement... 49 50 #include "Concurrency/Waitfor.h" // for generateWaitfor … … 345 346 PASS( "Implement Concurrent Keywords", Concurrency::implementKeywords, transUnit ); 346 347 PASS( "Fix Unique Ids", Validate::fixUniqueIds, transUnit ); 348 PASS( "Implement Corun", Concurrency::implementCorun, transUnit ); 347 349 PASS( "Hoist Control Declarations", ControlStruct::hoistControlDecls, transUnit ); 348 350 -
tests/.expect/linkonce.txt
r946a6e4 ra97b9ed 1 signed= -7 unsigned=121 signed=7 unsigned=12 -
tests/concurrency/cofor.cfa
r946a6e4 ra97b9ed 1 1 #include <cofor.hfa> 2 2 3 long total = 0; 4 void add_num( void * arg ) { __atomic_fetch_add( &total, (long)arg, __ATOMIC_SEQ_CST ); }3 4 void add_num( long * total, long val ) { __atomic_fetch_add( total, (long)val, __ATOMIC_SEQ_CST ); } 5 5 6 6 int main() { 7 7 printf("start\n"); 8 8 processor p[4]; 9 long total = 0; 9 10 COFOR( i, 0, 10, __atomic_fetch_add( &total, i, __ATOMIC_SEQ_CST ); ); 10 parallel_stmt_t stmts[5] = { add_num, add_num, add_num, add_num, add_num }; 11 void * nums[5] = { (void *)11, (void *)12, (void *)13, (void *)14, (void *)15 }; 12 parallel( stmts, nums, 5 ); 11 { 12 corun; // does nothing 13 corun{}; // does nothing 14 corun add_num( &total, 11 ); 15 corun { add_num( &total, 12 ); } 16 corun __atomic_fetch_add( &total, 13, __ATOMIC_SEQ_CST ); 17 corun { __atomic_fetch_add( &total, 14, __ATOMIC_SEQ_CST ); } 18 __atomic_fetch_add( &total, 15, __ATOMIC_SEQ_CST ); // run by main thd 19 } 13 20 printf("total: %ld\n", total); 14 21 printf("done\n"); -
tests/link-once/main.cfa
r946a6e4 ra97b9ed 1 1 // Test our new cfa_linkonce attribute: 2 2 3 __attribute__(( cfa_linkonce )) signed int example = -7;3 __attribute__(( cfa_linkonce )) signed int example = 7; 4 4 __attribute__(( cfa_linkonce )) unsigned int example = 12; 5 5 -
tests/link-once/partner.cfa
r946a6e4 ra97b9ed 1 1 // Side file for the link-once test. 2 2 3 __attribute__(( cfa_linkonce )) signed int example = -7;3 __attribute__(( cfa_linkonce )) signed int example = 7; 4 4 __attribute__(( cfa_linkonce )) unsigned int example = 12; 5 5 -
tests/test.py
r946a6e4 ra97b9ed 125 125 parser.add_argument('--archive-errors', help='If called with a valid path, on test crashes the test script will copy the core dump and the executable to the specified path.', type=str, default='') 126 126 parser.add_argument('-j', '--jobs', help='Number of tests to run simultaneously, 0 (default) for unlimited', nargs='?', const=0, type=int) 127 parser.add_argument('--list-comp', help='List all valid earguments', action='store_true')127 parser.add_argument('--list-comp', help='List all valid arguments', action='store_true') 128 128 parser.add_argument('--list-dist', help='List all tests for distribution', action='store_true') 129 129 parser.add_argument('-I','--include', help='Directory of test to include, can be used multiple time, All if omitted', action='append')
Note: See TracChangeset
for help on using the changeset viewer.