Example #1
0
// GetSourceNodes
//------------------------------------------------------------------------------
bool FunctionCopy::GetSourceNodes( const BFFIterator & iter, Node * node, Array< Node * > & nodes ) const
{
    if ( node->GetType() == Node::ALIAS_NODE )
    {
        // resolve aliases to real nodes
        AliasNode * aliasNode = node->CastTo< AliasNode >();
        const Dependencies & aliasedNodes = aliasNode->GetAliasedNodes();
        const Dependency * const end = aliasedNodes.End();
        for ( const Dependency * it = aliasedNodes.Begin(); it != end; ++it )
        {
            if ( !GetSourceNodes( iter, it->GetNode(), nodes ) )
            {
                return false;
            }
        }
        return true;
    }
    else if ( node->IsAFile() )
    {
        // anything that results in a file is ok
        nodes.Append( node );
        return true;
    }

    // something we don't know how to handle
    Error::Error_1005_UnsupportedNodeType( iter, this, ".Source", node->GetName(), node->GetType() );
    return false;
}
Example #2
0
void SchedulerCPU::LaunchTasks(const FrameContextEx& context, std::function<jobs::Future<std::any>(const FrameContextEx&, const Pipeline&, lemon::ListDigraph::Node, std::any)> onNode) {
	struct DependencyCounter {
	public:
		explicit DependencyCounter(size_t total = 0) {
			this->total = total;
		}
		void Reset() {
			*counter = 0;
		}
		bool operator++() {
			return (counter->fetch_add(1) + 1) == total;
		}

	private:
		std::shared_ptr<std::atomic_size_t> counter = std::make_shared<std::atomic_size_t>(0);
		std::size_t total = 0;
	};

	lemon::ListDigraph::NodeMap<DependencyCounter> depCounter(m_pipeline->GetTaskGraph());
	for (lemon::ListDigraph::NodeIt nodeIt(m_pipeline->GetTaskGraph()); nodeIt != lemon::INVALID; ++nodeIt) {
		depCounter[nodeIt] = DependencyCounter{ (size_t)lemon::countInArcs(m_pipeline->GetTaskGraph(), nodeIt) };
	}

	auto stopFlag = std::make_shared<std::atomic_bool>(false);
	auto Traverse = [this, &context, stopFlag, &onNode, &depCounter](lemon::ListDigraph::Node node, std::any passThrough, auto self) -> jobs::Future<void> {
		if (stopFlag->load()) {
			co_return;
		}

		try {
			std::any passToNext = co_await onNode(context, *m_pipeline, node, passThrough);

			std::vector<jobs::Future<void>> childJobs;
			for (lemon::ListDigraph::OutArcIt outArc(m_pipeline->GetTaskGraph(), node); outArc != lemon::INVALID; ++outArc) {
				auto nextNode = m_pipeline->GetTaskGraph().target(outArc);

				if (++depCounter[nextNode]) {
					childJobs.push_back(m_scheduler->Enqueue(self, nextNode, passToNext, self));
				}
			}

			for (auto& childFuture : childJobs) {
				co_await childFuture;
			}
		}
		catch (...) {
			stopFlag->store(true);
			throw;
		}
	};

	auto sourceNodes = GetSourceNodes(m_pipeline->GetTaskGraph());
	std::vector<jobs::Future<void>> childJobs;
	for (auto sourceNode : sourceNodes) {
		childJobs.push_back(m_scheduler->Enqueue(Traverse, sourceNode, std::any{}, Traverse));
	}

	std::vector<std::exception_ptr> exceptions;
	for (auto& job : childJobs) {
		try {
			job.get();
		}
		catch (...) {
			exceptions.push_back(std::current_exception());
		}
	}
	if (!exceptions.empty()) {
		// Rest of the exceptions is ignored.
		// TODO: throw some aggregate exception?
		std::rethrow_exception(exceptions[0]);
	}
}
Example #3
0
// Commit
//------------------------------------------------------------------------------
/*virtual*/ bool FunctionCopy::Commit( NodeGraph & nodeGraph, const BFFIterator & funcStartIter ) const
{
    // make sure all required variables are defined
    Array< AString > sources( 16, true );
    const BFFVariable * dstFileV;
    if ( !GetStrings( funcStartIter, sources, ".Source", true ) ||
         !GetString( funcStartIter, dstFileV, ".Dest", true ) )
    {
        return false; // GetString will have emitted errors
    }

    // Optional
    AStackString<> sourceBasePath;
    if ( !GetString( funcStartIter, sourceBasePath, ".SourceBasePath", false ) )
    {
        return false; // GetString will have emitted errors
    }

    // Canonicalize the SourceBasePath
    if ( !sourceBasePath.IsEmpty() )
    {
        AStackString<> cleanValue;
        NodeGraph::CleanPath( sourceBasePath, cleanValue );
        PathUtils::EnsureTrailingSlash( cleanValue );
        sourceBasePath = cleanValue;
    }

    // check sources are not paths
    {
        const AString * const end = sources.End();
        for ( const AString * it = sources.Begin(); it != end; ++it )
        {
            const AString & srcFile( *it );

            // source must be a file, not a  path
            if ( PathUtils::IsFolderPath( srcFile ) )
            {
                Error::Error_1105_PathNotAllowed( funcStartIter, this, ".Source", srcFile );
                return false;
            }
        }
    }

    // Pre-build dependencies
    Dependencies preBuildDependencies;
    if ( !GetNodeList( nodeGraph, funcStartIter, ".PreBuildDependencies", preBuildDependencies, false ) )
    {
        return false; // GetNodeList will have emitted an error
    }
    Array< AString > preBuildDependencyNames( preBuildDependencies.GetSize(), false );
    for ( const auto & dep : preBuildDependencies )
    {
        preBuildDependencyNames.Append( dep.GetNode()->GetName() );
    }

    // get source node
    Array< Node * > srcNodes;
    {
        const AString * const end = sources.End();
        for ( const AString * it = sources.Begin(); it != end; ++it )
        {

            Node * srcNode = nodeGraph.FindNode( *it );
            if ( srcNode )
            {
                if ( GetSourceNodes( funcStartIter, srcNode, srcNodes ) == false )
                {
                    return false;
                }
            }
            else
            {
                // source file not defined by use - assume an external file
                srcNodes.Append( nodeGraph.CreateFileNode( *it ) );
            }
        }
    }

    AStackString<> dstFile;
    NodeGraph::CleanPath( dstFileV->GetString(), dstFile );
    const bool dstIsFolderPath = PathUtils::IsFolderPath( dstFile );

    // make all the nodes for copies
    Dependencies copyNodes( srcNodes.GetSize(), false );
    for ( const Node * srcNode : srcNodes )
    {
        AStackString<> dst( dstFile );

        // dest can be a file OR a path.  If it's a path, use the source filename part
        if ( dstIsFolderPath )
        {
            // find filename part of source
            const AString & srcName = srcNode->GetName();

            // If the sourceBasePath is specified (and valid) use the name relative to that
            if ( !sourceBasePath.IsEmpty() && PathUtils::PathBeginsWith( srcName, sourceBasePath ) )
            {
                // Use everything relative to the SourceBasePath
                dst += srcName.Get() + sourceBasePath.GetLength();
            }
            else
            {
                // Use just the file name
                const char * lastSlash = srcName.FindLast( NATIVE_SLASH );
                dst += lastSlash ? ( lastSlash + 1 )    // append filename part if found
                                     : srcName.Get();   // otherwise append whole thing
            }
        }

        // check node doesn't already exist
        if ( nodeGraph.FindNode( dst ) )
        {
            // TODO:C could have a specific error for multiple sources with only 1 output
            // to differentiate from two rules creating the same dst target
            Error::Error_1100_AlreadyDefined( funcStartIter, this, dst );
            return false;
        }

        // create our node
        CopyFileNode * copyFileNode = nodeGraph.CreateCopyFileNode( dst );
        copyFileNode->m_Source = srcNode->GetName();
        copyFileNode->m_PreBuildDependencyNames = preBuildDependencyNames;
        if ( !copyFileNode->Initialize( nodeGraph, funcStartIter, this ) )
        {
            return false; // Initialize will have emitted an error
        }

        copyNodes.Append( Dependency( copyFileNode ) );
    }

    // handle alias creation
    return ProcessAlias( nodeGraph, funcStartIter, copyNodes );
}