Make utility using LINQ
Exit early

Instead of stuffing everything in one return statements, return as early as possible.

if (!projects.Any()) return Enumerable.Empty&lt;string&gt;();


can be the first statement of your method. It saves recreating the hashset objects and it makes the flow of your method with its recursion more clear. I think sacrificing a bit of "conciseness" for readability is worth it. 

ToArray

Are unnecessary and cause additional loops over your sets.

Except and HashSet

Enumerable.Except is already implemented using sets. The second argument is added to a set and the first is streamed through it. Creating the HashSet manually is a wasted effort. See the reference source.

So dependents can just be:

var dependents = references.Select(d =&gt; d.Dependent);


Contains and HashSet

Contains on the other hand benefits from the HashSet overload, since the Enumerable.Contains implementation just loops over the sequence. So build should be a HashSet to cope with larger inputs. While we're at it, we're using Linq, so ToHashSet().

static IEnumerable&lt;string&gt; Make(
    IEnumerable&lt;string&gt; projects,
    IEnumerable&lt;(string Dependency, string Dependent)&gt; references)
{
    if (!projects.Any())
    {
        return Enumerable.Empty&lt;string&gt;();
    }
    var dependents = references.Select(d =&gt; d.Dependent);
    var build = projects.Except(dependents).ToHashSet();
    if (!build.Any())
    {
        throw new Exception("Circled references detected.");
    }
    else
    {
        return build.Concat(Make(
              projects.Except(build),
              references.Where(r =&gt; !build.Contains(r.Dependency))));
    }
}